Hello,

Am Samstag, 15. November 2014 schrieb Christian Boltz:
> this patch changes aa.py and cleanprof.py to use the new capabiliy
> rule class.

The updated patch (v2) is attached.

Only change: The patch got updated to match the changed class and 
function names of the rule class.

This also means all notes from the first mail still apply.


Updated line count statistics:
1-add-base-and-capability-rule-class.diff - 365 lines added, 0 removed
2-add-capability-rule-test.diff - 747 lines added, 0 removed
3-use-capability-rule-class.diff - 62 lines added, 107 removed


Regards,

Christian Boltz
-- 
You took a sledge hammer to a meeting with the school principle and
found him carring a 9mm.  :^) [Patrick Shanahan in opensuse-factory]
=== modified file 'utils/apparmor/aa.py'
--- utils/apparmor/aa.py	2014-11-15 11:51:24 +0000
+++ utils/apparmor/aa.py	2014-11-21 20:37:58 +0000
@@ -52,6 +52,8 @@
 
 import apparmor.rules as aarules
 
+from apparmor.rule.capability import CapabilityRuleset, CapabilityRule
+
 from apparmor.yasti import SendDataToYast, GetDataFromYast, shutdown_yast
 
 # setup module translations
@@ -89,7 +91,7 @@
 # To store the globs entered by users so they can be provided again
 user_globs = []
 
-# The key for representing bare rules such as "capability," or "file,"
+# The key for representing bare "file," rules
 ALL = '\0ALL'
 
 ## Variables used under logprof
@@ -1628,16 +1630,16 @@
                                 if deleted:
                                     aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
 
-                            aa[profile][hat]['allow']['capability'][capability]['set'] = True
-                            aa[profile][hat]['allow']['capability'][capability]['audit'] = audit_toggle
+                            else:
+                                aa[profile][hat]['capability'].add_raw('%s capability %s,'% (audit, capability))  # XXX yes, ugly ;-)
+                                aaui.UI_Info(_('Adding capability %s to profile.') % capability)
 
                             changed[profile] = True
 
-                            aaui.UI_Info(_('Adding capability %s to profile.') % capability)
                             done = True
 
                         elif ans == 'CMD_DENY':
-                            aa[profile][hat]['deny']['capability'][capability]['set'] = True
+                            aa[profile][hat]['capability'].add_raw('%s deny capability %s,'% (audit, capability))  # XXX yes, ugly ;-)
                             changed[profile] = True
 
                             aaui.UI_Info(_('Denying capability %s to profile.') % capability)
@@ -2121,20 +2123,6 @@
                         deleted += 1
     return deleted
 
-def delete_cap_duplicates(profilecaps, inccaps):
-    deleted = []
-    if profilecaps and inccaps:
-        for capname in profilecaps.keys():
-            # XXX The presence of a bare capability rule ("capability,") should
-            #     cause more specific capability rules
-            #     ("capability audit_control,") to be deleted
-            if inccaps[capname].get('set', False) == 1:
-                deleted.append(capname)
-        for capname in deleted:
-            profilecaps.pop(capname)
-
-    return len(deleted)
-
 def delete_path_duplicates(profile, incname, allow):
     deleted = []
     for entry in profile[allow]['path'].keys():
@@ -2161,9 +2149,7 @@
 
         deleted += delete_net_duplicates(profile['deny']['netdomain'], include[incname][incname]['deny']['netdomain'])
 
-        deleted += delete_cap_duplicates(profile['allow']['capability'], include[incname][incname]['allow']['capability'])
-
-        deleted += delete_cap_duplicates(profile['deny']['capability'], include[incname][incname]['deny']['capability'])
+        deleted += profile['capability'].delete_duplicates(include[incname][incname]['capability'])
 
         deleted += delete_path_duplicates(profile, incname, 'allow')
         deleted += delete_path_duplicates(profile, incname, 'deny')
@@ -2173,9 +2159,7 @@
 
         deleted += delete_net_duplicates(profile['deny']['netdomain'], filelist[incname][incname]['deny']['netdomain'])
 
-        deleted += delete_cap_duplicates(profile['allow']['capability'], filelist[incname][incname]['allow']['capability'])
-
-        deleted += delete_cap_duplicates(profile['deny']['capability'], filelist[incname][incname]['deny']['capability'])
+        deleted += profile['capability'].delete_duplicates(filelist[incname][incname]['capability'])
 
         deleted += delete_path_duplicates(profile, incname, 'allow')
         deleted += delete_path_duplicates(profile, incname, 'deny')
@@ -2206,7 +2190,9 @@
 def match_cap_includes(profile, cap):
     newincludes = []
     for incname in include.keys():
-        if valid_include(profile, incname) and include[incname][incname]['allow']['capability'][cap].get('set', False) == 1:
+        # XXX type check should go away once we init all profiles correctly
+        # XXX using a CapabilityRule object instead of is_raw_covered() might be nice/faster
+        if valid_include(profile, incname) and type(include[incname][incname]['capability']) == CapabilityRuleset and include[incname][incname]['capability'].is_raw_covered("capability %s," % cap):
             newincludes.append(incname)
 
     return newincludes
@@ -2516,7 +2502,8 @@
 
                 for capability in prelog[aamode][profile][hat]['capability'].keys():
                     # If capability not already in profile
-                    if not aa[profile][hat]['allow']['capability'][capability].get('set', False):
+                    # XXX remove first check when we have proper profile initialisation
+                    if aa[profile][hat].get('capability', False) and not aa[profile][hat]['capability'].is_raw_covered("capability %s," % capability):
                         log_dict[aamode][profile][hat]['capability'][capability] = True
 
                 nd = prelog[aamode][profile][hat]['netdomain']
@@ -2704,6 +2691,10 @@
                 profile_data[profile][profile]['repo']['url'] = repo_data['url']
                 profile_data[profile][profile]['repo']['user'] = repo_data['user']
 
+            # init rule classes (if not done yet)
+            if not profile_data[profile][hat].get('capability', False):
+                profile_data[profile][hat]['capability'] = CapabilityRuleset()
+
         elif RE_PROFILE_END.search(line):
             # If profile ends and we're not in one
             if not profile:
@@ -2719,21 +2710,14 @@
             initial_comment = ''
 
         elif RE_PROFILE_CAP.search(line):
-            matches = RE_PROFILE_CAP.search(line)
-
             if not profile:
                 raise AppArmorException(_('Syntax Error: Unexpected capability entry found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 })
 
-            audit, allow, allow_keyword, comment = parse_audit_allow(matches)
-            # TODO: honor allow_keyword and comment
-
-            capability = ALL
-            if matches.group('capability'):
-                capability = matches.group('capability').strip()
-                # TODO: can contain more than one capability- split it?
-
-            profile_data[profile][hat][allow]['capability'][capability]['set'] = True
-            profile_data[profile][hat][allow]['capability'][capability]['audit'] = audit
+            # init rule class (if not done yet)
+            if not profile_data[profile][hat].get('capability', False):
+                profile_data[profile][hat]['capability'] = CapabilityRuleset()
+
+            profile_data[profile][hat]['capability'].add_raw(line)
 
         elif RE_PROFILE_LINK.search(line):
             matches = RE_PROFILE_LINK.search(line).groups()
@@ -3373,29 +3357,8 @@
 def write_list_vars(prof_data, depth):
     return write_pair(prof_data, depth, '', 'lvar', '', ' = ', '', var_transform)
 
-def write_cap_rules(prof_data, depth, allow):
-    pre = '  ' * depth
-    data = []
-    allowstr = set_allow_str(allow)
-
-    if prof_data[allow].get('capability', False):
-        for cap in sorted(prof_data[allow]['capability'].keys()):
-            audit = ''
-            if prof_data[allow]['capability'][cap].get('audit', False):
-                audit = 'audit '
-            if prof_data[allow]['capability'][cap].get('set', False):
-                if cap == ALL:
-                    data.append('%s%s%scapability,' % (pre, audit, allowstr))
-                else:
-                    data.append('%s%s%scapability %s,' % (pre, audit, allowstr, cap))
-        data.append('')
-
-    return data
-
 def write_capabilities(prof_data, depth):
-    #data = write_single(prof_data, depth, '', 'set_capability', 'set capability ', ',')
-    data = write_cap_rules(prof_data, depth, 'deny')
-    data += write_cap_rules(prof_data, depth, 'allow')
+    data = prof_data['capability'].get_clean(depth)
     return data
 
 def write_net_rules(prof_data, depth, allow):
@@ -3886,6 +3849,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
 
                     # then write everything else
                     for segs in default_write_order:
@@ -3927,25 +3892,7 @@
                     profile = None
 
             elif RE_PROFILE_CAP.search(line):
-                matches = RE_PROFILE_CAP.search(line).groups()
-                audit = False
-                if matches[0]:
-                    audit = matches[0]
-
-                allow = 'allow'
-                if matches[1] and matches[1].strip() == 'deny':
-                    allow = 'deny'
-
-                capability = ALL
-                if matches[2]:
-                    capability = matches[2].strip()
-
-                if not write_prof_data[hat][allow]['capability'][capability].get('set', False):
-                    correct = False
-                if not write_prof_data[hat][allow]['capability'][capability].get(audit, False) == audit:
-                    correct = False
-
-                if correct:
+                if write_prof_data[hat]['capability'].is_raw_covered(line, True, True):
                     if not segments['capability'] and True in segments.values():
                         for segs in list(filter(lambda x: segments[x], segments.keys())):
                             depth = len(line) - len(line.lstrip())
@@ -3955,13 +3902,11 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['capability'] = True
-                    write_prof_data[hat][allow]['capability'].pop(capability)
+                    write_prof_data[hat]['capability'].delete_raw(line)
                     data.append(line)
-
-                    #write_prof_data[hat][allow]['capability'][capability].pop(audit)
-
-                    #Remove this line
                 else:
                     # To-Do
                     pass
@@ -3996,6 +3941,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['link'] = True
                     write_prof_data[hat][allow]['link'].pop(link)
                     data.append(line)
@@ -4020,6 +3967,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['change_profile'] = True
                     write_prof_data[hat]['change_profile'].pop(cp)
                     data.append(line)
@@ -4050,6 +3999,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['alias'] = True
                     if profile:
                         write_prof_data[hat]['alias'].pop(from_name)
@@ -4079,6 +4030,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['rlimit'] = True
                     write_prof_data[hat]['rlimit'].pop(from_name)
                     data.append(line)
@@ -4104,6 +4057,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['lvar'] = True
                     write_prof_data[hat]['lvar'].pop(bool_var)
                     data.append(line)
@@ -4135,6 +4090,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['lvar'] = True
                     if profile:
                         write_prof_data[hat]['lvar'].pop(list_var)
@@ -4173,6 +4130,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['path'] = True
                     write_prof_data[hat][allow]['path'].pop(ALL)
                     data.append(line)
@@ -4221,6 +4180,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['path'] = True
                     write_prof_data[hat][allow]['path'].pop(path)
                     data.append(line)
@@ -4241,6 +4202,8 @@
                                     write_prof_data[name]['allow'].pop(segs)
                                 if write_prof_data[name]['deny'].get(segs, False):
                                     write_prof_data[name]['deny'].pop(segs)
+                                if write_prof_data[name].get(segs, False):
+                                    write_prof_data[name][segs].delete_all_rules()
                             segments['include'] = True
                         write_prof_data[hat]['include'].pop(include_name)
                         data.append(line)
@@ -4294,6 +4257,8 @@
                                 write_prof_data[name]['allow'].pop(segs)
                             if write_prof_data[name]['deny'].get(segs, False):
                                 write_prof_data[name]['deny'].pop(segs)
+                            if write_prof_data[name].get(segs, False):
+                                write_prof_data[name][segs].delete_all_rules()
                     segments['netdomain'] = True
 
             elif RE_PROFILE_CHANGE_HAT.search(line):
@@ -4408,19 +4373,20 @@
     return 0
 
 def profile_known_capability(profile, capname):
-    if profile['deny']['capability'][capname].get('set', False):
-        return -1
+    rule = CapabilityRule()
+    rule.set_raw('capability %s,' % capname)  # having a CapabilityRule object as parameter would be even better ;-)
 
-    if profile['allow']['capability'][capname].get('set', False):
-        return 1
+    # XXX get rid of type checks after we have a proper function to initialize a profile
+    if type(profile['capability']) == CapabilityRuleset:
+        if profile['capability'].is_obj_covered(rule, False):
+            return True
 
     for incname in profile['include'].keys():
-        if include[incname][incname]['deny']['capability'][capname].get('set', False):
-            return -1
-        if include[incname][incname]['allow']['capability'][capname].get('set', False):
-            return 1
+        if type(include[incname][incname]['capability']) == CapabilityRuleset:
+            if include[incname][incname]['capability'].is_obj_covered(rule, False):
+                return True
 
-    return 0
+    return False
 
 def profile_known_network(profile, family, sock_type):
     if netrules_access_check(profile['deny']['netdomain'], family, sock_type):

=== modified file 'utils/apparmor/cleanprofile.py'
--- utils/apparmor/cleanprofile.py	2014-09-05 21:21:00 +0000
+++ utils/apparmor/cleanprofile.py	2014-11-21 19:08:06 +0000
@@ -65,8 +65,8 @@
                 deleted += apparmor.aa.delete_duplicates(self.other.aa[program][hat], inc)
 
             #Clean the duplicates of caps in other profile
-            deleted += delete_cap_duplicates(self.profile.aa[program][hat]['allow']['capability'], self.other.aa[program][hat]['allow']['capability'], self.same_file)
-            deleted += delete_cap_duplicates(self.profile.aa[program][hat]['deny']['capability'], self.other.aa[program][hat]['deny']['capability'], self.same_file)
+            if self.same_file:
+                deleted += self.other.aa[program][hat]['capability'].delete_duplicates(self.profile.aa[program][hat]['capability'])
 
             #Clean the duplicates of path in other profile
             deleted += delete_path_duplicates(self.profile.aa[program][hat], self.other.aa[program][hat], 'allow', self.same_file)
@@ -108,17 +108,6 @@
 
     return len(deleted)
 
-def delete_cap_duplicates(profilecaps, profilecaps_other, same_profile=True):
-    deleted = []
-    if profilecaps and profilecaps_other and not same_profile:
-        for capname in profilecaps.keys():
-            if profilecaps_other[capname].get('set', False):
-                deleted.append(capname)
-        for capname in deleted:
-            profilecaps_other.pop(capname)
-
-    return len(deleted)
-
 def delete_net_duplicates(netrules, netrules_other, same_profile=True):
     deleted = 0
     hasher_obj = apparmor.aa.hasher()

-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to