[apparmor] [patch] aa.py: use named match groups for capability

Christian Boltz apparmor at cboltz.de
Fri Sep 26 20:00:33 UTC 2014


Hello,

this patch converts RE_PROFILE_CAP in aa.py and the code using it to 
named match groups.

(capability is one of the easiest rule types, so it's good as a start.)

The patch also adds basic support for rules containing more than one 
capability, like
    capability chown dac_override,
Note that this is just a pass-through mode (instead of complaining about 
an invalid line). aa-logprof will happily add another "capability chown" 
if it hits a log entry for it. (But: we never got a bugreport about not 
supporting multi-capability lines, so I guess they are rarely used ;-)

I also added a parse_audit_allow() function to handle the audit and 
allow/deny keywords. They are used in most rule types, which means we 
can get rid of some duplicated code with this function.


Finally, update utils/test/test-regex_matches.py - RE_PROFILE_CAP now 
has 5 instead of 4 match groups because of the added multi-capability 
support.

While on it, I also improved the error message in setup_regex_tests()
to also show the rule that causes a problem.


Feel free to comment about named match groups and the other changes 
introduced with this patch in general. Capability is the "prototype", 
similar patches for other rule types will follow sooner or later.
(I won't complain if someone "grabs" one of the rule types so that I
don't have to do everything ;-)


=== modified file 'utils/apparmor/aa.py'                                                                                                                                         
--- utils/apparmor/aa.py        2014-09-22 21:41:54 +0000                                                                                                                        
+++ utils/apparmor/aa.py        2014-09-26 18:58:57 +0000                                                                                                                        
@@ -2618,7 +2627,7 @@
 
 RE_PROFILE_START        = re.compile('^\s*("?(/.+?)"??|(profile\s+"?(.+?)"??))\s+((flags=)?\((.+)\)\s+)?\{' + RE_EOL)
 RE_PROFILE_END          = re.compile('^\s*\}' + RE_EOL)
-RE_PROFILE_CAP          = re.compile(RE_AUDIT_DENY + 'capability(\s+\S+)?' + RE_COMMA_EOL)
+RE_PROFILE_CAP          = re.compile(RE_AUDIT_DENY + 'capability(?P<capability>(\s+\S+)+)?' + RE_COMMA_EOL)
 RE_PROFILE_LINK         = re.compile(RE_AUDIT_DENY + 'link\s+(((subset)|(<=))\s+)?([\"\@\/].*?"??)\s+->\s*([\"\@\/].*?"??)' + RE_COMMA_EOL)
 RE_PROFILE_CHANGE_PROFILE = re.compile('^\s*change_profile\s+->\s*("??.+?"??)' + RE_COMMA_EOL)
 RE_PROFILE_ALIAS        = re.compile('^\s*alias\s+("??.+?"??)\s+->\s*("??.+?"??)' + RE_COMMA_EOL)
 @@ -2747,22 +2759,18 @@
             initial_comment = ''
 
         elif RE_PROFILE_CAP.search(line):
-            matches = RE_PROFILE_CAP.search(line).groups()
+            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 = False
-            if matches[0]:
-                audit = True
-
-            allow = 'allow'
-            if matches[1] and matches[1].strip() == 'deny':
-                allow = 'deny'
+            audit, allow, allow_keyword = parse_audit_allow(matches)
+            # TODO: honor allow_keyword
 
             capability = ALL
-            if matches[2]:
-                capability = matches[2].strip()
+            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
@@ -3216,6 +3224,21 @@
 
     return profile_data
 
+def parse_audit_allow(matches):
+    audit = False
+    if matches.group('audit'):
+        audit = True
+
+    allow = 'allow'
+    allow_keyword = False
+    if matches.group('allow'):
+        allow = matches.group('allow').strip()
+        allow_keyword = True
+        if allow != 'allow' and allow != 'deny':  # should never happen
+            raise AppArmorException(_("Invalid allow/deny keyword %s" % allow))
+    
+    return (audit, allow, allow_keyword)
+
 # RE_DBUS_ENTRY = re.compile('^dbus\s*()?,\s*$')
 #   use stuff like '(?P<action>(send|write|w|receive|read|r|rw))'
 

=== modified file 'utils/test/test-regex_matches.py'
--- utils/test/test-regex_matches.py    2014-09-04 01:22:04 +0000
+++ utils/test/test-regex_matches.py    2014-09-26 19:24:14 +0000
@@ -162,7 +162,7 @@
     for (i, group) in enumerate(groups):
         if group:
             group = group.strip()
-        self.assertEqual(group, expected[i], 'Group %d mismatch' % i)
+        self.assertEqual(group, expected[i], 'Group %d mismatch in rule %s' % (i,line))
 
 
 def setup_regex_tests(test_class):
@@ -188,10 +188,10 @@
         self.regex = aa.RE_PROFILE_CAP
 
     tests = [
-        ('   capability net_raw,', (None, None, 'net_raw', None)),
-        ('capability     net_raw   ,  ', (None, None, 'net_raw', None)),
-        ('   capability,', (None, None, None, None)),
-        ('   capability   ,  ', (None, None, None, None)),
+        ('   capability net_raw,', (None, None, 'net_raw', 'net_raw', None)),
+        ('capability     net_raw   ,  ', (None, None, 'net_raw', 'net_raw', None)),
+        ('   capability,', (None, None, None, None, None)),
+        ('   capability   ,  ', (None, None, None, None, None)),
         ('   capabilitynet_raw,', False)
     ]
 


Regards,

Christian Boltz
-- 
Der geistige Horizont ist der Abstand zwischen Brett und Kopf.




More information about the AppArmor mailing list