Hello,

Am Freitag, 5. September 2014 schrieb Kshitij Gupta:
> Seems I sent out that patch a bit early, a secondary consolidated
> patch which fixes (hopefully) the issue multiple profiles in a file is
> attached.
> (Brace a for loop is removed resulting in a huge diff due to the
> indent change).

As discussed in yesterday's meeting, we'd like to include this patch in 
the 2.9 final release that is planned for this week (!)

I just updated the patch to apply on latest bzr[1] - the result is 
attached as 
    mergeprof2-rebased.diff. 

I'm also attaching
    mergeprof2-rebased-diff-ignore-whitespace.diff
which is the same patch made with "diff -w" to ignore whitespace 
changes. It's probably not a surprise that it's much shorter and easier 
to read ;-)


That already gave me a mostly working aa-mergeprof, but I soon found out 
why Kshitij said the patch is not final yet ;-)

I did the following additional fixes:
- remove some debug output (which Kshitij intentionally kept in the 
  draft patch)
- add a UI_Info to display which profile will be merged
- disable the mergeprofiles.clear_common() call because it crashes
- disable (M)ore (CMD_OTHER) because it crashes
- make (F)inish work everywhere
- change the help text so that it doesn't mention 3-way-merge until we
  implement it

Those changes are in mergeprof-fixes.diff which applies on top of 
mergeprof2-rebased.diff.


Remaining known issues I noticed while testing:
- aa-mergeprof always asks for includes, even if they are already there
- CMD_OTHER should work ;-)


Regards,

Christian Boltz

[1] actually I applied the patch against r2636, and then re-applied the 
    two commits after that
-- 
[IP-Adresse von ppp0 mit system() ermitteln]
Dazu Perl zu verwenden, ähnelt sicherlich ein wenig der Spatzenjagd
mit großkalibrigen Langrohrgeschützen...;-)
[Christian Schmidt in suse-linux]
=== modified file 'utils/aa-mergeprof'
--- utils/aa-mergeprof	2014-10-07 13:06:01 +0000
+++ utils/aa-mergeprof	2014-10-15 19:08:03 +0000
@@ -28,17 +28,17 @@
 
 parser = argparse.ArgumentParser(description=_('Perform a 2-way or 3-way merge on the given profiles'),
     epilog='WARNING: the arguments will change in a future version!')
-parser.add_argument('mine', type=str, help=_('your profile'))
-parser.add_argument('base', type=str, help=_('base profile'))
-parser.add_argument('other', nargs='?', type=str, help=_('other profile'))
+parser.add_argument('files', nargs='+', type=str, help=_('base profile'))
+#parser.add_argument('other', nargs='?', type=str, help=_('other profile'))
 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
 # 2-way merge or 3-way merge based on number of params
-merge_mode = 2 if args.other == None else 3
+merge_mode = 2 #if args.other == None else 3
 
-profiles = [args.mine, args.base, args.other]
+profiles = [args.files, [args.other]]
 
 profiledir = args.dir
 if profiledir:
@@ -46,25 +46,94 @@
     if not os.path.isdir(apparmor.aa.profile_dir):
         raise apparmor.AppArmorException(_("%s is not a directory.") %profiledir)
 
+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
 
 def main():
-    mergeprofiles = Merge(profiles)
+    profiles_to_merge = set()
+
+    base_files, other_files = profiles
+
+    base_profile_to_file = find_profiles_from_files(base_files)
+
+    profiles_to_merge = profiles_to_merge.union(set(base_profile_to_file.keys()))
+
+    other_profile_to_file = dict()
+
+    if merge_mode == 3:
+        other_profile_to_file = find_profiles_from_files(other_files)
+        profiles_to_merge.add(other_profile_to_file.keys())
+
+    user_profile_to_file = find_files_from_profiles(profiles_to_merge)
+
+    print(base_files,"\n",other_files)
+    print(base_profile_to_file,"\n",other_profile_to_file,"\n",user_profile_to_file)
+    print(profiles_to_merge)
+
+    for profile_name in profiles_to_merge:
+        user_file = user_profile_to_file[profile_name]
+        base_file = base_profile_to_file.get(profile_name, None)
+        other_file =  None
+
+        if merge_mode == 3:
+            other_file = other_profile_to_file.get(profile_name, None)
+
+        if base_file == None:
+            if other_file == None:
+                continue
+
+            act([user_file, other_file, None], 2, profile_name)
+        else:
+            if other_file == None:
+                act([user_file, base_file, None], 2, profile_name)
+            else:
+                act([user_file, base_file, other_file], 3, profile_name)
+
+        reset_aa()
+
+def act(files, merge_mode, merging_profile):
+    mergeprofiles = Merge(files)
     #Get rid of common/superfluous stuff
     mergeprofiles.clear_common()
 
 #    if not args.auto:
     if 1 == 1:  # workaround to avoid lots of whitespace changes
         if merge_mode == 3:
-            mergeprofiles.ask_the_questions('other')
+            mergeprofiles.ask_the_questions('other', merging_profile)
 
             mergeprofiles.clear_common()
 
-        mergeprofiles.ask_the_questions('base')
+        mergeprofiles.ask_the_questions('base', merging_profile)
 
         q = aaui.PromptQuestion()
         q.title = _('Changed Local Profiles')
         q.explanation = _('The following local profiles were changed. Would you like to save them?')
-        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
+        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY']
         q.default = 'CMD_VIEW_CHANGES'
         q.options = []
         q.selected = 0
@@ -84,6 +153,8 @@
                 #oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, '')
                 newprofile = apparmor.aa.serialize_profile(mergeprofiles.user.aa[program], program, '')
                 apparmor.aa.display_changes_with_comments(mergeprofiles.user.filename, newprofile)
+            elif ans == 'CMD_IGNORE_ENTRY':
+                break
 
 
 class Merge(object):
@@ -94,25 +165,18 @@
         apparmor.aa.read_profile(base, True)
         self.base = cleanprofile.Prof(base)
 
-        self.reset()
+        reset_aa()
 
         #Read and parse other profile and save profile data, include data from it and reset them
         if merge_mode == 3:
             apparmor.aa.read_profile(other, True)
             self.other = cleanprofile.Prof(other)
-            self.reset()
+            reset_aa()
 
         #Read and parse user profile
         apparmor.aa.read_profile(user, True)
         self.user = cleanprofile.Prof(user)
 
-    def reset(self):
-        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 clear_common(self):
         deleted = 0
 
@@ -166,7 +230,7 @@
                             raise apparmor.aa.AppArmorException(_('Unknown selection'))
                         done = True
 
-    def ask_the_questions(self, other):
+    def ask_the_questions(self, other, profile):
         if other == 'other':
             other = self.other
         else:
@@ -199,501 +263,501 @@
         sev_db = apparmor.aa.sev_db
         if not sev_db:
             sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
-        for profile in sorted(other.aa.keys()):
-            for hat in sorted(other.aa[profile].keys()):
-                #Add the includes from the other profile to the user profile
-                done = False
-                options = list(map(lambda inc: '#include <%s>' %inc, sorted(other.aa[profile][hat]['include'].keys())))
-                default_option = 1
-
-                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'
-
-                while not done and options:
-                    ans, selected = q.promptUser()
-                    if ans == 'CMD_IGNORE_ENTRY':
-                        done = True
-                    elif ans == 'CMD_ALLOW':
-                        selection = options[selected]
-                        inc = apparmor.aa.re_match_include(selection)
-                        deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
-                        self.user.aa[profile][hat]['include'][inc] = True
-                        options.pop(selected)
-                        aaui.UI_Info(_('Adding %s to the file.') % selection)
-                        if deleted:
-                            aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
-
-                #Add the capabilities
-                for allow in ['allow', 'deny']:
-                    if other.aa[profile][hat].get(allow, False):
-                        continue
-                    for capability in sorted(other.aa[profile][hat][allow]['capability'].keys()):
-                        severity = sev_db.rank('CAP_%s' % capability)
+
+        for hat in sorted(other.aa[profile].keys()):
+            #Add the includes from the other profile to the user profile
+            done = False
+            options = list(map(lambda inc: '#include <%s>' %inc, sorted(other.aa[profile][hat]['include'].keys())))
+            default_option = 1
+
+            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'
+
+            while not done and options:
+                ans, selected = q.promptUser()
+                if ans == 'CMD_IGNORE_ENTRY':
+                    done = True
+                elif ans == 'CMD_ALLOW':
+                    selection = options[selected]
+                    inc = apparmor.aa.re_match_include(selection)
+                    deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
+                    self.user.aa[profile][hat]['include'][inc] = True
+                    options.pop(selected)
+                    aaui.UI_Info(_('Adding %s to the file.') % selection)
+                    if deleted:
+                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
+
+            #Add the capabilities
+            for allow in ['allow', 'deny']:
+                if other.aa[profile][hat].get(allow, False):
+                    continue
+                for capability in sorted(other.aa[profile][hat][allow]['capability'].keys()):
+                    severity = sev_db.rank('CAP_%s' % capability)
+                    default_option = 1
+                    options = []
+                    newincludes = apparmor.aa.match_cap_includes(self.user.aa[profile][hat], capability)
+                    q = aaui.PromptQuestion()
+                    if newincludes:
+                        options += list(map(lambda inc: '#include <%s>' %inc, sorted(set(newincludes))))
+
+                    if options:
+                        options.append('capability %s' % capability)
+                        q.options = options
+                        q.selected = default_option - 1
+
+                    q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
+                    q.headers += [_('Capability'), capability]
+                    q.headers += [_('Severity'), severity]
+
+                    audit_toggle = 0
+
+                    q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
+
+                    q.default = 'CMD_ALLOW'
+
+                    done = False
+                    while not done:
+                        ans, selected = q.promptUser()
+                        # Ignore the log entry
+                        if ans == 'CMD_IGNORE_ENTRY':
+                            done = True
+                            break
+
+                        if ans == 'CMD_ALLOW':
+                            selection = ''
+                            if options:
+                                selection = options[selected]
+                            match = apparmor.aa.re_match_include(selection)
+                            if match:
+                                deleted = False
+                                inc = match
+                                deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
+                                self.user.aa[profile][hat]['include'][inc] = True
+
+                                aaui.UI_Info(_('Adding %s to profile.') % selection)
+                                if deleted:
+                                    aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
+
+                            self.user.aa[profile][hat]['allow']['capability'][capability]['set'] = True
+                            self.user.aa[profile][hat]['allow']['capability'][capability]['audit'] = other.aa[profile][hat]['allow']['capability'][capability]['audit']
+
+                            apparmor.aa.changed[profile] = True
+
+                            aaui.UI_Info(_('Adding capability %s to profile.'), capability)
+                            done = True
+
+                        elif ans == 'CMD_DENY':
+                            self.user.aa[profile][hat]['deny']['capability'][capability]['set'] = True
+                            apparmor.aa.changed[profile] = True
+
+                            aaui.UI_Info(_('Denying capability %s to profile.') % capability)
+                            done = True
+                        else:
+                            done = False
+
+            # Process all the path entries.
+            for allow in ['allow', 'deny']:
+                for path in sorted(other.aa[profile][hat][allow]['path'].keys()):
+                    #print(path, other.aa[profile][hat][allow]['path'][path])
+                    mode = other.aa[profile][hat][allow]['path'][path]['mode']
+
+                    if self.user.aa[profile][hat][allow]['path'].get(path, False):
+                        mode = self.conflict_mode(profile, hat, allow, path, 'mode', other.aa[profile][hat][allow]['path'][path]['mode'], self.user.aa[profile][hat][allow]['path'][path]['mode'])
+                        self.conflict_mode(profile, hat, allow, path, 'audit', other.aa[profile][hat][allow]['path'][path]['audit'], self.user.aa[profile][hat][allow]['path'][path]['audit'])
+                        apparmor.aa.changed[profile] = True
+                        continue
+                    # Lookup modes from profile
+                    allow_mode = set()
+                    allow_audit = set()
+                    deny_mode = set()
+                    deny_audit = set()
+
+                    fmode, famode, fm = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'allow', path)
+                    if fmode:
+                        allow_mode |= fmode
+                    if famode:
+                        allow_audit |= famode
+
+                    cm, cam, m = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'deny', path)
+                    if cm:
+                        deny_mode |= cm
+                    if cam:
+                        deny_audit |= cam
+
+                    imode, iamode, im = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'allow', path)
+                    if imode:
+                        allow_mode |= imode
+                    if iamode:
+                        allow_audit |= iamode
+
+                    cm, cam, m = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'deny', path)
+                    if cm:
+                        deny_mode |= cm
+                    if cam:
+                        deny_audit |= cam
+
+                    if deny_mode & apparmor.aamode.AA_MAY_EXEC:
+                        deny_mode |= apparmor.aamode.ALL_AA_EXEC_TYPE
+
+                    # Mask off the denied modes
+                    mode = mode - deny_mode
+
+                    # If we get an exec request from some kindof event that generates 'PERMITTING X'
+                    # check if its already in allow_mode
+                    # if not add ix permission
+                    if mode & apparmor.aamode.AA_MAY_EXEC:
+                        # Remove all type access permission
+                        mode = mode - apparmor.aamode.ALL_AA_EXEC_TYPE
+                        if not allow_mode & apparmor.aamode.AA_MAY_EXEC:
+                            mode |= apparmor.aa.str_to_mode('ix')
+
+                    # m is not implied by ix
+
+                    ### If we get an mmap request, check if we already have it in allow_mode
+                    ##if mode & AA_EXEC_MMAP:
+                    ##    # ix implies m, so we don't need to add m if ix is present
+                    ##    if contains(allow_mode, 'ix'):
+                    ##        mode = mode - AA_EXEC_MMAP
+
+                    if not mode:
+                        continue
+
+                    matches = []
+
+                    if fmode:
+                        matches += fm
+
+                    if imode:
+                        matches += im
+
+                    if not apparmor.aa.mode_contains(allow_mode, mode):
                         default_option = 1
                         options = []
-                        newincludes = apparmor.aa.match_cap_includes(self.user.aa[profile][hat], capability)
-                        q = aaui.PromptQuestion()
+                        newincludes = []
+                        include_valid = False
+
+                        for incname in apparmor.aa.include.keys():
+                            include_valid = False
+                            # If already present skip
+                            if self.user.aa[profile][hat][incname]:
+                                continue
+                            if incname.startswith(apparmor.aa.profile_dir):
+                                incname = incname.replace(apparmor.aa.profile_dir+'/', '', 1)
+
+                            include_valid = apparmor.aa.valid_include('', incname)
+
+                            if not include_valid:
+                                continue
+
+                            cm, am, m = apparmor.aa.match_include_to_path(incname, 'allow', path)
+
+                            if cm and apparmor.aa.mode_contains(cm, mode):
+                                dm = apparmor.aa.match_include_to_path(incname, 'deny', path)[0]
+                                # If the mode is denied
+                                if not mode & dm:
+                                    if not list(filter(lambda s: '/**' == s, m)):
+                                        newincludes.append(incname)
+                        # Add new includes to the options
                         if newincludes:
-                            options += list(map(lambda inc: '#include <%s>' %inc, sorted(set(newincludes))))
-
-                        if options:
-                            options.append('capability %s' % capability)
-                            q.options = options
-                            q.selected = default_option - 1
-
-                        q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
-                        q.headers += [_('Capability'), capability]
-                        q.headers += [_('Severity'), severity]
+                            options += list(map(lambda s: '#include <%s>' % s, sorted(set(newincludes))))
+                        # We should have literal the path in options list too
+                        options.append(path)
+                        # Add any the globs matching path from logprof
+                        globs = apparmor.aa.glob_common(path)
+                        if globs:
+                            matches += globs
+                        # Add any user entered matching globs
+                        for user_glob in apparmor.aa.user_globs:
+                            if apparmor.aa.matchliteral(user_glob, path):
+                                matches.append(user_glob)
+
+                        matches = list(set(matches))
+                        if path in matches:
+                            matches.remove(path)
+
+                        options += apparmor.aa.order_globs(matches, path)
+                        default_option = len(options)
+
+                        sev_db.unload_variables()
+                        sev_db.load_variables(apparmor.aa.get_profile_filename(profile))
+                        severity = sev_db.rank(path, apparmor.aa.mode_to_str(mode))
+                        sev_db.unload_variables()
 
                         audit_toggle = 0
-
-                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
-
-                        q.default = 'CMD_ALLOW'
-
+                        owner_toggle = 0
+                        if apparmor.aa.cfg['settings']['default_owner_prompt']:
+                            owner_toggle = apparmor.aa.cfg['settings']['default_owner_prompt']
                         done = False
                         while not done:
+                            q =  aaui.PromptQuestion()
+                            q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat),
+                                            _('Path'), path]
+
+                            if allow_mode:
+                                mode |= allow_mode
+                                tail = ''
+                                s = ''
+                                prompt_mode = None
+                                if owner_toggle == 0:
+                                    prompt_mode = apparmor.aa.flatten_mode(mode)
+                                    tail = '     ' + _('(owner permissions off)')
+                                elif owner_toggle == 1:
+                                    prompt_mode = mode
+                                elif owner_toggle == 2:
+                                    prompt_mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
+                                    tail = '     ' + _('(force new perms to owner)')
+                                else:
+                                    prompt_mode = apparmor.aa.owner_flatten_mode(mode)
+                                    tail = '     ' + _('(force all rule perms to owner)')
+
+                                if audit_toggle == 1:
+                                    s = apparmor.aa.mode_to_str_user(allow_mode)
+                                    if allow_mode:
+                                        s += ', '
+                                    s += 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode - allow_mode) + tail
+                                elif audit_toggle == 2:
+                                    s = 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode) + tail
+                                else:
+                                    s = apparmor.aa.mode_to_str_user(prompt_mode) + tail
+
+                                q.headers += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
+                                                 _('New Mode'), s]
+
+                            else:
+                                s = ''
+                                tail = ''
+                                prompt_mode = None
+                                if audit_toggle:
+                                    s = 'audit'
+                                if owner_toggle == 0:
+                                    prompt_mode = apparmor.aa.flatten_mode(mode)
+                                    tail = '     ' + _('(owner permissions off)')
+                                elif owner_toggle == 1:
+                                    prompt_mode = mode
+                                else:
+                                    prompt_mode = apparmor.aa.owner_flatten_mode(mode)
+                                    tail = '     ' + _('(force perms to owner)')
+
+                                s = apparmor.aa.mode_to_str_user(prompt_mode)
+                                q.headers += [_('Mode'), s]
+
+                            q.headers += [_('Severity'), severity]
+                            q.options = options
+                            q.selected = default_option - 1
+                            q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
+                                              'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
+                                              'CMD_FINISHED', 'CMD_OTHER']
+
+                            q.default = 'CMD_ALLOW'
+
+
                             ans, selected = q.promptUser()
-                            # Ignore the log entry
+
                             if ans == 'CMD_IGNORE_ENTRY':
                                 done = True
                                 break
 
-                            if ans == 'CMD_ALLOW':
-                                selection = ''
-                                if options:
-                                    selection = options[selected]
-                                match = apparmor.aa.re_match_include(selection)
+                            if ans == 'CMD_OTHER':
+                                audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
+                            elif ans == 'CMD_USER_TOGGLE':
+                                owner_toggle += 1
+                                if not allow_mode and owner_toggle == 2:
+                                    owner_toggle += 1
+                                if owner_toggle > 3:
+                                    owner_toggle = 0
+                            elif ans == 'CMD_ALLOW':
+                                path = options[selected]
+                                done = True
+                                match = apparmor.aa.re_match_include(path)
                                 if match:
-                                    deleted = False
                                     inc = match
+                                    deleted = 0
                                     deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
-                                    self.user.aa[profile][hat]['include'][inc] = True
-
-                                    aaui.UI_Info(_('Adding %s to profile.') % selection)
+                                    self.user.aa[profile][hat]['include'][inc] =  True
+                                    apparmor.aa.changed[profile] =  True
+                                    aaui.UI_Info(_('Adding %s to profile.') % path)
                                     if deleted:
                                         aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
 
-                                self.user.aa[profile][hat]['allow']['capability'][capability]['set'] = True
-                                self.user.aa[profile][hat]['allow']['capability'][capability]['audit'] = other.aa[profile][hat]['allow']['capability'][capability]['audit']
-
-                                apparmor.aa.changed[profile] = True
-
-                                aaui.UI_Info(_('Adding capability %s to profile.'), capability)
-                                done = True
-
-                            elif ans == 'CMD_DENY':
-                                self.user.aa[profile][hat]['deny']['capability'][capability]['set'] = True
-                                apparmor.aa.changed[profile] = True
-
-                                aaui.UI_Info(_('Denying capability %s to profile.') % capability)
-                                done = True
-                            else:
-                                done = False
-
-                # Process all the path entries.
-                for allow in ['allow', 'deny']:
-                    for path in sorted(other.aa[profile][hat][allow]['path'].keys()):
-                        #print(path, other.aa[profile][hat][allow]['path'][path])
-                        mode = other.aa[profile][hat][allow]['path'][path]['mode']
-
-                        if self.user.aa[profile][hat][allow]['path'].get(path, False):
-                            mode = self.conflict_mode(profile, hat, allow, path, 'mode', other.aa[profile][hat][allow]['path'][path]['mode'], self.user.aa[profile][hat][allow]['path'][path]['mode'])
-                            self.conflict_mode(profile, hat, allow, path, 'audit', other.aa[profile][hat][allow]['path'][path]['audit'], self.user.aa[profile][hat][allow]['path'][path]['audit'])
-                            apparmor.aa.changed[profile] = True
-                            continue
-                        # Lookup modes from profile
-                        allow_mode = set()
-                        allow_audit = set()
-                        deny_mode = set()
-                        deny_audit = set()
-
-                        fmode, famode, fm = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'allow', path)
-                        if fmode:
-                            allow_mode |= fmode
-                        if famode:
-                            allow_audit |= famode
-
-                        cm, cam, m = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'deny', path)
-                        if cm:
-                            deny_mode |= cm
-                        if cam:
-                            deny_audit |= cam
-
-                        imode, iamode, im = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'allow', path)
-                        if imode:
-                            allow_mode |= imode
-                        if iamode:
-                            allow_audit |= iamode
-
-                        cm, cam, m = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'deny', path)
-                        if cm:
-                            deny_mode |= cm
-                        if cam:
-                            deny_audit |= cam
-
-                        if deny_mode & apparmor.aamode.AA_MAY_EXEC:
-                            deny_mode |= apparmor.aamode.ALL_AA_EXEC_TYPE
-
-                        # Mask off the denied modes
-                        mode = mode - deny_mode
-
-                        # If we get an exec request from some kindof event that generates 'PERMITTING X'
-                        # check if its already in allow_mode
-                        # if not add ix permission
-                        if mode & apparmor.aamode.AA_MAY_EXEC:
-                            # Remove all type access permission
-                            mode = mode - apparmor.aamode.ALL_AA_EXEC_TYPE
-                            if not allow_mode & apparmor.aamode.AA_MAY_EXEC:
-                                mode |= apparmor.aa.str_to_mode('ix')
-
-                        # m is not implied by ix
-
-                        ### If we get an mmap request, check if we already have it in allow_mode
-                        ##if mode & AA_EXEC_MMAP:
-                        ##    # ix implies m, so we don't need to add m if ix is present
-                        ##    if contains(allow_mode, 'ix'):
-                        ##        mode = mode - AA_EXEC_MMAP
-
-                        if not mode:
-                            continue
-
-                        matches = []
-
-                        if fmode:
-                            matches += fm
-
-                        if imode:
-                            matches += im
-
-                        if not apparmor.aa.mode_contains(allow_mode, mode):
-                            default_option = 1
-                            options = []
-                            newincludes = []
-                            include_valid = False
-
-                            for incname in apparmor.aa.include.keys():
-                                include_valid = False
-                                # If already present skip
-                                if self.user.aa[profile][hat][incname]:
-                                    continue
-                                if incname.startswith(apparmor.aa.profile_dir):
-                                    incname = incname.replace(apparmor.aa.profile_dir+'/', '', 1)
-
-                                include_valid = apparmor.aa.valid_include('', incname)
-
-                                if not include_valid:
-                                    continue
-
-                                cm, am, m = apparmor.aa.match_include_to_path(incname, 'allow', path)
-
-                                if cm and apparmor.aa.mode_contains(cm, mode):
-                                    dm = apparmor.aa.match_include_to_path(incname, 'deny', path)[0]
-                                    # If the mode is denied
-                                    if not mode & dm:
-                                        if not list(filter(lambda s: '/**' == s, m)):
-                                            newincludes.append(incname)
-                            # Add new includes to the options
-                            if newincludes:
-                                options += list(map(lambda s: '#include <%s>' % s, sorted(set(newincludes))))
-                            # We should have literal the path in options list too
-                            options.append(path)
-                            # Add any the globs matching path from logprof
-                            globs = apparmor.aa.glob_common(path)
-                            if globs:
-                                matches += globs
-                            # Add any user entered matching globs
-                            for user_glob in apparmor.aa.user_globs:
-                                if apparmor.aa.matchliteral(user_glob, path):
-                                    matches.append(user_glob)
-
-                            matches = list(set(matches))
-                            if path in matches:
-                                matches.remove(path)
-
-                            options += apparmor.aa.order_globs(matches, path)
-                            default_option = len(options)
-
-                            sev_db.unload_variables()
-                            sev_db.load_variables(apparmor.aa.get_profile_filename(profile))
-                            severity = sev_db.rank(path, apparmor.aa.mode_to_str(mode))
-                            sev_db.unload_variables()
-
-                            audit_toggle = 0
-                            owner_toggle = 0
-                            if apparmor.aa.cfg['settings']['default_owner_prompt']:
-                                owner_toggle = apparmor.aa.cfg['settings']['default_owner_prompt']
-                            done = False
-                            while not done:
-                                q =  aaui.PromptQuestion()
-                                q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat),
-                                                _('Path'), path]
-
-                                if allow_mode:
-                                    mode |= allow_mode
-                                    tail = ''
-                                    s = ''
-                                    prompt_mode = None
+                                else:
+                                    if self.user.aa[profile][hat]['allow']['path'][path].get('mode', False):
+                                        mode |= self.user.aa[profile][hat]['allow']['path'][path]['mode']
+                                    deleted = []
+                                    for entry in self.user.aa[profile][hat]['allow']['path'].keys():
+                                        if path == entry:
+                                            continue
+
+                                        if apparmor.aa.matchregexp(path, entry):
+                                            if apparmor.aa.mode_contains(mode, self.user.aa[profile][hat]['allow']['path'][entry]['mode']):
+                                                deleted.append(entry)
+                                    for entry in deleted:
+                                        self.user.aa[profile][hat]['allow']['path'].pop(entry)
+                                    deleted = len(deleted)
+
                                     if owner_toggle == 0:
-                                        prompt_mode = apparmor.aa.flatten_mode(mode)
-                                        tail = '     ' + _('(owner permissions off)')
-                                    elif owner_toggle == 1:
-                                        prompt_mode = mode
+                                        mode = apparmor.aa.flatten_mode(mode)
+                                    #elif owner_toggle == 1:
+                                    #    mode = mode
                                     elif owner_toggle == 2:
-                                        prompt_mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
-                                        tail = '     ' + _('(force new perms to owner)')
-                                    else:
-                                        prompt_mode = apparmor.aa.owner_flatten_mode(mode)
-                                        tail = '     ' + _('(force all rule perms to owner)')
-
+                                        mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
+                                    elif owner_toggle == 3:
+                                        mode = apparmor.aa.owner_flatten_mode(mode)
+
+                                    if not self.user.aa[profile][hat]['allow'].get(path, False):
+                                        self.user.aa[profile][hat]['allow']['path'][path]['mode'] = self.user.aa[profile][hat]['allow']['path'][path].get('mode', set()) | mode
+
+
+                                    tmpmode = set()
                                     if audit_toggle == 1:
-                                        s = apparmor.aa.mode_to_str_user(allow_mode)
-                                        if allow_mode:
-                                            s += ', '
-                                        s += 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode - allow_mode) + tail
+                                        tmpmode = mode- allow_mode
                                     elif audit_toggle == 2:
-                                        s = 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode) + tail
-                                    else:
-                                        s = apparmor.aa.mode_to_str_user(prompt_mode) + tail
-
-                                    q.headers += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
-                                                     _('New Mode'), s]
-
-                                else:
-                                    s = ''
-                                    tail = ''
-                                    prompt_mode = None
-                                    if audit_toggle:
-                                        s = 'audit'
-                                    if owner_toggle == 0:
-                                        prompt_mode = apparmor.aa.flatten_mode(mode)
-                                        tail = '     ' + _('(owner permissions off)')
-                                    elif owner_toggle == 1:
-                                        prompt_mode = mode
-                                    else:
-                                        prompt_mode = apparmor.aa.owner_flatten_mode(mode)
-                                        tail = '     ' + _('(force perms to owner)')
-
-                                    s = apparmor.aa.mode_to_str_user(prompt_mode)
-                                    q.headers += [_('Mode'), s]
-
-                                q.headers += [_('Severity'), severity]
-                                q.options = options
-                                q.selected = default_option - 1
-                                q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
-                                                  'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
-                                                  'CMD_FINISHED', 'CMD_OTHER']
-
-                                q.default = 'CMD_ALLOW'
-
-
-                                ans, selected = q.promptUser()
-
-                                if ans == 'CMD_IGNORE_ENTRY':
-                                    done = True
-                                    break
-
-                                if ans == 'CMD_OTHER':
-                                    audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
-                                elif ans == 'CMD_USER_TOGGLE':
-                                    owner_toggle += 1
-                                    if not allow_mode and owner_toggle == 2:
-                                        owner_toggle += 1
-                                    if owner_toggle > 3:
-                                        owner_toggle = 0
-                                elif ans == 'CMD_ALLOW':
-                                    path = options[selected]
-                                    done = True
-                                    match = apparmor.aa.re_match_include(path)
-                                    if match:
-                                        inc = match
-                                        deleted = 0
-                                        deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
-                                        self.user.aa[profile][hat]['include'][inc] =  True
-                                        apparmor.aa.changed[profile] =  True
-                                        aaui.UI_Info(_('Adding %s to profile.') % path)
-                                        if deleted:
-                                            aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
-
-                                    else:
-                                        if self.user.aa[profile][hat]['allow']['path'][path].get('mode', False):
-                                            mode |= self.user.aa[profile][hat]['allow']['path'][path]['mode']
-                                        deleted = []
-                                        for entry in self.user.aa[profile][hat]['allow']['path'].keys():
-                                            if path == entry:
-                                                continue
-
-                                            if apparmor.aa.matchregexp(path, entry):
-                                                if apparmor.aa.mode_contains(mode, self.user.aa[profile][hat]['allow']['path'][entry]['mode']):
-                                                    deleted.append(entry)
-                                        for entry in deleted:
-                                            self.user.aa[profile][hat]['allow']['path'].pop(entry)
-                                        deleted = len(deleted)
-
-                                        if owner_toggle == 0:
-                                            mode = apparmor.aa.flatten_mode(mode)
-                                        #elif owner_toggle == 1:
-                                        #    mode = mode
-                                        elif owner_toggle == 2:
-                                            mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
-                                        elif owner_toggle == 3:
-                                            mode = apparmor.aa.owner_flatten_mode(mode)
-
-                                        if not self.user.aa[profile][hat]['allow'].get(path, False):
-                                            self.user.aa[profile][hat]['allow']['path'][path]['mode'] = self.user.aa[profile][hat]['allow']['path'][path].get('mode', set()) | mode
-
-
-                                        tmpmode = set()
-                                        if audit_toggle == 1:
-                                            tmpmode = mode- allow_mode
-                                        elif audit_toggle == 2:
-                                            tmpmode = mode
-
-                                        self.user.aa[profile][hat]['allow']['path'][path]['audit'] = self.user.aa[profile][hat]['allow']['path'][path].get('audit', set()) | tmpmode
-
-                                        apparmor.aa.changed[profile] = True
-
-                                        aaui.UI_Info(_('Adding %(path)s %(mode)s to profile') % { 'path': path, 'mode': apparmor.aa.mode_to_str_user(mode) })
-                                        if deleted:
-                                            aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
-
-                                elif ans == 'CMD_DENY':
-                                    path = options[selected].strip()
-                                    # Add new entry?
-                                    self.user.aa[profile][hat]['deny']['path'][path]['mode'] = self.user.aa[profile][hat]['deny']['path'][path].get('mode', set()) | (mode - allow_mode)
-
-                                    self.user.aa[profile][hat]['deny']['path'][path]['audit'] = self.user.aa[profile][hat]['deny']['path'][path].get('audit', set())
+                                        tmpmode = mode
+
+                                    self.user.aa[profile][hat]['allow']['path'][path]['audit'] = self.user.aa[profile][hat]['allow']['path'][path].get('audit', set()) | tmpmode
 
                                     apparmor.aa.changed[profile] = True
 
-                                    done = True
-
-                                elif ans == 'CMD_NEW':
-                                    arg = options[selected]
-                                    if not apparmor.aa.re_match_include(arg):
-                                        ans = aaui.UI_GetString(_('Enter new path: '), arg)
+                                    aaui.UI_Info(_('Adding %(path)s %(mode)s to profile') % { 'path': path, 'mode': apparmor.aa.mode_to_str_user(mode) })
+                                    if deleted:
+                                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
+
+                            elif ans == 'CMD_DENY':
+                                path = options[selected].strip()
+                                # Add new entry?
+                                self.user.aa[profile][hat]['deny']['path'][path]['mode'] = self.user.aa[profile][hat]['deny']['path'][path].get('mode', set()) | (mode - allow_mode)
+
+                                self.user.aa[profile][hat]['deny']['path'][path]['audit'] = self.user.aa[profile][hat]['deny']['path'][path].get('audit', set())
+
+                                apparmor.aa.changed[profile] = True
+
+                                done = True
+
+                            elif ans == 'CMD_NEW':
+                                arg = options[selected]
+                                if not apparmor.aa.re_match_include(arg):
+                                    ans = aaui.UI_GetString(_('Enter new path: '), arg)
 #                                         if ans:
 #                                             if not matchliteral(ans, path):
 #                                                 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)
 #                                                 key = aaui.UI_YesNo(ynprompt, 'n')
 #                                                 if key == 'n':
 #                                                     continue
-                                        apparmor.aa.user_globs.append(ans)
-                                        options.append(ans)
-                                        default_option = len(options)
-
-                                elif ans == 'CMD_GLOB':
-                                    newpath = options[selected].strip()
-                                    if not apparmor.aa.re_match_include(newpath):
-                                        newpath = apparmor.aa.glob_path(newpath)
-
-                                        if newpath not in options:
-                                            options.append(newpath)
-                                            default_option = len(options)
-                                        else:
-                                            default_option = options.index(newpath) + 1
-
-                                elif ans == 'CMD_GLOBEXT':
-                                    newpath = options[selected].strip()
-                                    if not apparmor.aa.re_match_include(newpath):
-                                        newpath = apparmor.aa.glob_path_withext(newpath)
-
-                                        if newpath not in options:
-                                            options.append(newpath)
-                                            default_option = len(options)
-                                        else:
-                                            default_option = options.index(newpath) + 1
-
-                                elif re.search('\d', ans):
-                                    default_option = ans
-
-                #
-                for allow in ['allow', 'deny']:
-                    for family in sorted(other.aa[profile][hat][allow]['netdomain']['rule'].keys()):
-                        # severity handling for net toggles goes here
-
-                        for sock_type in sorted(other.aa[profile][hat][allow]['netdomain']['rule'][family].keys()):
-                            if apparmor.aa.profile_known_network(self.user.aa[profile][hat], family, sock_type):
-                                continue
-                            default_option = 1
-                            options = []
-                            newincludes = apparmor.aa.match_net_includes(self.user.aa[profile][hat], family, sock_type)
-                            q = aaui.PromptQuestion()
-                            if newincludes:
-                                options += list(map(lambda s: '#include <%s>'%s, sorted(set(newincludes))))
-                            if True:#options:
-                                options.append('network %s %s' % (family, sock_type))
-                                q.options = options
-                                q.selected = default_option - 1
-
-                            q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
-                            q.headers += [_('Network Family'), family]
-                            q.headers += [_('Socket Type'), sock_type]
-
-                            audit_toggle = 0
-                            q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
-                                              'CMD_ABORT', 'CMD_FINISHED']
-
-                            q.default = 'CMD_ALLOW'
-
-                            done = False
-                            while not done:
-                                ans, selected = q.promptUser()
-                                if ans == 'CMD_IGNORE_ENTRY':
-                                    done = True
-                                    break
-
-                                if ans.startswith('CMD_AUDIT'):
-                                    audit_toggle = not audit_toggle
-                                    audit = ''
-                                    if audit_toggle:
-                                        audit = 'audit'
-                                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
-                                                          'CMD_ABORT', 'CMD_FINISHED']
-                                    else:
-                                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
-                                                          'CMD_ABORT', 'CMD_FINISHED']
-                                    q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
-                                    q.headers += [_('Network Family'), audit + family]
-                                    q.headers += [_('Socket Type'), sock_type]
-
-                                elif ans == 'CMD_ALLOW':
-                                    #print(options, selected)
-                                    selection = options[selected]
-                                    done = True
-                                    if apparmor.aa.re_match_include(selection): #re.search('#include\s+<.+>$', selection):
-                                        inc =  apparmor.aa.re_match_include(selection) #re.search('#include\s+<(.+)>$', selection).groups()[0]
-                                        deleted =  0
-                                        deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
-
-                                        self.user.aa[profile][hat]['include'][inc] = True
-
-                                        apparmor.aa.changed[profile] = True
-
-                                        aaui.UI_Info(_('Adding %s to profile') % selection)
-                                        if deleted:
-                                            aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
-
-                                    else:
-                                        self.user.aa[profile][hat]['allow']['netdomain']['audit'][family][sock_type] = audit_toggle
-                                        self.user.aa[profile][hat]['allow']['netdomain']['rule'][family][sock_type] = True
-
-                                        apparmor.aa.changed[profile] = True
-
-                                        aaui.UI_Info(_('Adding network access %(family)s %(type)s to profile.') % { 'family': family, 'type': sock_type })
-
-                                elif ans == 'CMD_DENY':
-                                    done = True
-                                    self.user.aa[profile][hat]['deny']['netdomain']['rule'][family][sock_type] = True
-                                    apparmor.aa.changed[profile] = True
-                                    aaui.UI_Info(_('Denying network access %(family)s %(type)s to profile') % { 'family': family, 'type': sock_type })
-
-                                else:
-                                    done = False
+                                    apparmor.aa.user_globs.append(ans)
+                                    options.append(ans)
+                                    default_option = len(options)
+
+                            elif ans == 'CMD_GLOB':
+                                newpath = options[selected].strip()
+                                if not apparmor.aa.re_match_include(newpath):
+                                    newpath = apparmor.aa.glob_path(newpath)
+
+                                    if newpath not in options:
+                                        options.append(newpath)
+                                        default_option = len(options)
+                                    else:
+                                        default_option = options.index(newpath) + 1
+
+                            elif ans == 'CMD_GLOBEXT':
+                                newpath = options[selected].strip()
+                                if not apparmor.aa.re_match_include(newpath):
+                                    newpath = apparmor.aa.glob_path_withext(newpath)
+
+                                    if newpath not in options:
+                                        options.append(newpath)
+                                        default_option = len(options)
+                                    else:
+                                        default_option = options.index(newpath) + 1
+
+                            elif re.search('\d', ans):
+                                default_option = ans
+
+            #
+            for allow in ['allow', 'deny']:
+                for family in sorted(other.aa[profile][hat][allow]['netdomain']['rule'].keys()):
+                    # severity handling for net toggles goes here
+
+                    for sock_type in sorted(other.aa[profile][hat][allow]['netdomain']['rule'][family].keys()):
+                        if apparmor.aa.profile_known_network(self.user.aa[profile][hat], family, sock_type):
+                            continue
+                        default_option = 1
+                        options = []
+                        newincludes = apparmor.aa.match_net_includes(self.user.aa[profile][hat], family, sock_type)
+                        q = aaui.PromptQuestion()
+                        if newincludes:
+                            options += list(map(lambda s: '#include <%s>'%s, sorted(set(newincludes))))
+                        if True:#options:
+                            options.append('network %s %s' % (family, sock_type))
+                            q.options = options
+                            q.selected = default_option - 1
+
+                        q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
+                        q.headers += [_('Network Family'), family]
+                        q.headers += [_('Socket Type'), sock_type]
+
+                        audit_toggle = 0
+                        q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_AUDIT_NEW',
+                                          'CMD_ABORT', 'CMD_FINISHED']
+
+                        q.default = 'CMD_ALLOW'
+
+                        done = False
+                        while not done:
+                            ans, selected = q.promptUser()
+                            if ans == 'CMD_IGNORE_ENTRY':
+                                done = True
+                                break
+
+                            if ans.startswith('CMD_AUDIT'):
+                                audit_toggle = not audit_toggle
+                                audit = ''
+                                if audit_toggle:
+                                    audit = 'audit'
+                                    q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_OFF',
+                                                      'CMD_ABORT', 'CMD_FINISHED']
+                                else:
+                                    q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_AUDIT_NEW',
+                                                      'CMD_ABORT', 'CMD_FINISHED']
+                                q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
+                                q.headers += [_('Network Family'), audit + family]
+                                q.headers += [_('Socket Type'), sock_type]
+
+                            elif ans == 'CMD_ALLOW':
+                                #print(options, selected)
+                                selection = options[selected]
+                                done = True
+                                if apparmor.aa.re_match_include(selection): #re.search('#include\s+<.+>$', selection):
+                                    inc =  apparmor.aa.re_match_include(selection) #re.search('#include\s+<(.+)>$', selection).groups()[0]
+                                    deleted =  0
+                                    deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
+
+                                    self.user.aa[profile][hat]['include'][inc] = True
+
+                                    apparmor.aa.changed[profile] = True
+
+                                    aaui.UI_Info(_('Adding %s to profile') % selection)
+                                    if deleted:
+                                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
+
+                                else:
+                                    self.user.aa[profile][hat]['allow']['netdomain']['audit'][family][sock_type] = audit_toggle
+                                    self.user.aa[profile][hat]['allow']['netdomain']['rule'][family][sock_type] = True
+
+                                    apparmor.aa.changed[profile] = True
+
+                                    aaui.UI_Info(_('Adding network access %(family)s %(type)s to profile.') % { 'family': family, 'type': sock_type })
+
+                            elif ans == 'CMD_DENY':
+                                done = True
+                                self.user.aa[profile][hat]['deny']['netdomain']['rule'][family][sock_type] = True
+                                apparmor.aa.changed[profile] = True
+                                aaui.UI_Info(_('Denying network access %(family)s %(type)s to profile') % { 'family': family, 'type': sock_type })
+
+                            else:
+                                done = False
 
 if __name__ == '__main__':
     main()

--- aa-mergeprof	2014-10-15 22:02:03.608738000 +0200
+++ aa-mergeprof__MERGED	2014-10-15 21:08:12.610435497 +0200
@@ -28,17 +28,17 @@ _ = init_translation()
 
 parser = argparse.ArgumentParser(description=_('Perform a 2-way or 3-way merge on the given profiles'),
     epilog='WARNING: the arguments will change in a future version!')
-parser.add_argument('mine', type=str, help=_('your profile'))
-parser.add_argument('base', type=str, help=_('base profile'))
-parser.add_argument('other', nargs='?', type=str, help=_('other profile'))
+parser.add_argument('files', nargs='+', type=str, help=_('base profile'))
+#parser.add_argument('other', nargs='?', type=str, help=_('other profile'))
 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
 # 2-way merge or 3-way merge based on number of params
-merge_mode = 2 if args.other == None else 3
+merge_mode = 2 #if args.other == None else 3
 
-profiles = [args.mine, args.base, args.other]
+profiles = [args.files, [args.other]]
 
 profiledir = args.dir
 if profiledir:
@@ -46,25 +46,94 @@ if profiledir:
     if not os.path.isdir(apparmor.aa.profile_dir):
         raise apparmor.AppArmorException(_("%s is not a directory.") %profiledir)
 
+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
 
 def main():
-    mergeprofiles = Merge(profiles)
+    profiles_to_merge = set()
+
+    base_files, other_files = profiles
+
+    base_profile_to_file = find_profiles_from_files(base_files)
+
+    profiles_to_merge = profiles_to_merge.union(set(base_profile_to_file.keys()))
+
+    other_profile_to_file = dict()
+
+    if merge_mode == 3:
+        other_profile_to_file = find_profiles_from_files(other_files)
+        profiles_to_merge.add(other_profile_to_file.keys())
+
+    user_profile_to_file = find_files_from_profiles(profiles_to_merge)
+
+    print(base_files,"\n",other_files)
+    print(base_profile_to_file,"\n",other_profile_to_file,"\n",user_profile_to_file)
+    print(profiles_to_merge)
+
+    for profile_name in profiles_to_merge:
+        user_file = user_profile_to_file[profile_name]
+        base_file = base_profile_to_file.get(profile_name, None)
+        other_file =  None
+
+        if merge_mode == 3:
+            other_file = other_profile_to_file.get(profile_name, None)
+
+        if base_file == None:
+            if other_file == None:
+                continue
+
+            act([user_file, other_file, None], 2, profile_name)
+        else:
+            if other_file == None:
+                act([user_file, base_file, None], 2, profile_name)
+            else:
+                act([user_file, base_file, other_file], 3, profile_name)
+
+        reset_aa()
+
+def act(files, merge_mode, merging_profile):
+    mergeprofiles = Merge(files)
     #Get rid of common/superfluous stuff
     mergeprofiles.clear_common()
 
 #    if not args.auto:
     if 1 == 1:  # workaround to avoid lots of whitespace changes
         if merge_mode == 3:
-            mergeprofiles.ask_the_questions('other')
+            mergeprofiles.ask_the_questions('other', merging_profile)
 
             mergeprofiles.clear_common()
 
-        mergeprofiles.ask_the_questions('base')
+        mergeprofiles.ask_the_questions('base', merging_profile)
 
         q = aaui.PromptQuestion()
         q.title = _('Changed Local Profiles')
         q.explanation = _('The following local profiles were changed. Would you like to save them?')
-        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT']
+        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY']
         q.default = 'CMD_VIEW_CHANGES'
         q.options = []
         q.selected = 0
@@ -84,6 +153,8 @@ def main():
                 #oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, '')
                 newprofile = apparmor.aa.serialize_profile(mergeprofiles.user.aa[program], program, '')
                 apparmor.aa.display_changes_with_comments(mergeprofiles.user.filename, newprofile)
+            elif ans == 'CMD_IGNORE_ENTRY':
+                break
 
 
 class Merge(object):
@@ -94,25 +165,18 @@ class Merge(object):
         apparmor.aa.read_profile(base, True)
         self.base = cleanprofile.Prof(base)
 
-        self.reset()
+        reset_aa()
 
         #Read and parse other profile and save profile data, include data from it and reset them
         if merge_mode == 3:
             apparmor.aa.read_profile(other, True)
             self.other = cleanprofile.Prof(other)
-            self.reset()
+            reset_aa()
 
         #Read and parse user profile
         apparmor.aa.read_profile(user, True)
         self.user = cleanprofile.Prof(user)
 
-    def reset(self):
-        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 clear_common(self):
         deleted = 0
 
@@ -166,7 +230,7 @@ class Merge(object):
                             raise apparmor.aa.AppArmorException(_('Unknown selection'))
                         done = True
 
-    def ask_the_questions(self, other):
+    def ask_the_questions(self, other, profile):
         if other == 'other':
             other = self.other
         else:
@@ -199,7 +263,7 @@ class Merge(object):
         sev_db = apparmor.aa.sev_db
         if not sev_db:
             sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
-        for profile in sorted(other.aa.keys()):
+
             for hat in sorted(other.aa[profile].keys()):
                 #Add the includes from the other profile to the user profile
                 done = False
--- aa-mergeprof__MERGED	2014-10-15 21:08:12.610435497 +0200
+++ aa-mergeprof	2014-10-15 22:08:18.942940939 +0200
@@ -26,8 +26,7 @@ import apparmor.ui as aaui
 from apparmor.translations import init_translation
 _ = init_translation()
 
-parser = argparse.ArgumentParser(description=_('Perform a 2-way or 3-way merge on the given profiles'),
-    epilog='WARNING: the arguments will change in a future version!')
+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=_('base profile'))
 #parser.add_argument('other', nargs='?', type=str, help=_('other profile'))
 parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
@@ -91,11 +90,12 @@ def main():
 
     user_profile_to_file = find_files_from_profiles(profiles_to_merge)
 
-    print(base_files,"\n",other_files)
-    print(base_profile_to_file,"\n",other_profile_to_file,"\n",user_profile_to_file)
-    print(profiles_to_merge)
+#    print(base_files,"\n",other_files)
+#    print(base_profile_to_file,"\n",other_profile_to_file,"\n",user_profile_to_file)
+#    print(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)
         other_file =  None
@@ -119,7 +119,26 @@ def main():
 def act(files, merge_mode, merging_profile):
     mergeprofiles = Merge(files)
     #Get rid of common/superfluous stuff
-    mergeprofiles.clear_common()
+#    mergeprofiles.clear_common()
+# temporarily disabled because...
+
+# Traceback (most recent call last):
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/aa-mergeprof", line 764, in <module>
+#     main()
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/aa-mergeprof", line 114, in main
+#     act([user_file, base_file, None], 2, profile_name)
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/aa-mergeprof", line 123, in act
+#     mergeprofiles.clear_common()
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/aa-mergeprof", line 191, in clear_common
+#     deleted += user_base.compare_profiles()
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/apparmor/cleanprofile.py", line 42, in compare_profiles
+#     deleted += self.remove_duplicate_rules(profile)
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/apparmor/cleanprofile.py", line 72, in remove_duplicate_rules
+#     deleted += delete_path_duplicates(self.profile.aa[program][hat], self.other.aa[program][hat], 'allow', self.same_file)
+#   File "/home/cb/apparmor/HEAD-CLEAN/utils/apparmor/cleanprofile.py", line 107, in delete_path_duplicates
+#     profile_other[allow]['path'].pop(entry)
+# KeyError: '/etc/postfix/ssl/certs/cboltz-key.pem'
+
 
 #    if not args.auto:
     if 1 == 1:  # workaround to avoid lots of whitespace changes
@@ -250,7 +269,7 @@ class Merge(object):
         q.default = 'CMD_ALLOW'
 
         while not done and options:
-            ans, selected = q.PromptUser()
+            ans, selected = q.promptUser()
             if ans == 'CMD_IGNORE_ENTRY':
                 done = True
             elif ans == 'CMD_ALLOW':
@@ -259,6 +278,8 @@ class Merge(object):
                 self.user.filelist[self.user.filename]['include'][inc] = True
                 options.pop(selected)
                 aaui.UI_Info(_('Adding %s to the file.') % selection)
+            elif ans == 'CMD_FINISHED':
+                return
 
         sev_db = apparmor.aa.sev_db
         if not sev_db:
@@ -290,6 +311,8 @@ class Merge(object):
                     aaui.UI_Info(_('Adding %s to the file.') % selection)
                     if deleted:
                         aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
+                elif ans == 'CMD_FINISHED':
+                    return
 
             #Add the capabilities
             for allow in ['allow', 'deny']:
@@ -327,6 +350,9 @@ class Merge(object):
                             done = True
                             break
 
+                        elif ans == 'CMD_FINISHED':
+                            return
+
                         if ans == 'CMD_ALLOW':
                             selection = ''
                             if options:
@@ -561,8 +587,15 @@ class Merge(object):
                                 done = True
                                 break
 
+                            elif ans == 'CMD_FINISHED':
+                                return
+
                             if ans == 'CMD_OTHER':
-                                audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
+                                aaui.UI_Important("Sorry, not implemented yet!")
+                                # audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
+# crashes with
+#    audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
+#    AttributeError: 'module' object has no attribute 'UI_ask_mode_toggles'
                             elif ans == 'CMD_USER_TOGGLE':
                                 owner_toggle += 1
                                 if not allow_mode and owner_toggle == 2:
@@ -711,6 +744,9 @@ class Merge(object):
                                 done = True
                                 break
 
+                            elif ans == 'CMD_FINISHED':
+                                return
+
                             if ans.startswith('CMD_AUDIT'):
                                 audit_toggle = not audit_toggle
                                 audit = ''
-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to