[apparmor] [patch 10/11] utils: add very limited dbus rule support

Steve Beattie steve at nxnw.org
Thu Mar 6 01:44:44 UTC 2014


This patch adds very limited support for very dumb parsing of dbus
rules. Basically, it stores dbus rules as raw strings wrapped in
a class.

There's class structure to indicate how I'd like to see fuller future
support for dbus rules to be implemented and act as a guidepost for
how to handle most rules, moving away from the giant structure of
nested dictionaries. A stub test script is included as well, with a
modification to the make check target to set the PYTHONPATH to point
in the right place.

With this patch, aa-audit, aa-autodep, aa-complain, aa-disable,
and aa-enforce all function for me. aa-logprof and aa-genprof have
functionality issues for me at the moment (one of them dumps a
backtrace even without this patch), and I'm not sure the writing out
of dbus rules is completely implemented for modified profiles.

Signed-off-by: Steve Beattie <steve at nxnw.org>
---
 utils/apparmor/aa.py          |   64 ++++++++++++++++++++++++++++++++++++++++++
 utils/apparmor/rules.py       |   57 +++++++++++++++++++++++++++++++++++++
 utils/test/Makefile           |    2 -
 utils/test/test-dbus_parse.py |   30 +++++++++++++++++++
 4 files changed, 152 insertions(+), 1 deletion(-)

Index: b/utils/apparmor/aa.py
===================================================================
--- a/utils/apparmor/aa.py
+++ b/utils/apparmor/aa.py
@@ -39,6 +39,8 @@ from apparmor.aamode import (str_to_mode
                              mode_to_str_user, mode_contains, AA_OTHER,
                              flatten_mode, owner_flatten_mode)
 
+import apparmor.rules as aarules
+
 from apparmor.yasti import SendDataToYast, GetDataFromYast, shutdown_yast
 
 # setup module translations
@@ -2613,6 +2615,7 @@ RE_PROFILE_CHANGE_HAT = re.compile('^\s*
 RE_PROFILE_HAT_DEF = re.compile('^\s*\^(\"??.+?\"??)\s+((flags=)?\((.+)\)\s+)*\{\s*(#.*)?$')
 RE_NETWORK_FAMILY_TYPE = re.compile('\s+(\S+)\s+(\S+)\s*,$')
 RE_NETWORK_FAMILY = re.compile('\s+(\S+)\s*,$')
+RE_PROFILE_DBUS = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?(dbus[^#]*)\s*(#.*)?$')
 
 def parse_profile_data(data, file, do_include):
     profile_data = hasher()
@@ -2676,6 +2679,7 @@ def parse_profile_data(data, file, do_in
 
             profile_data[profile][hat]['allow']['netdomain'] = hasher()
             profile_data[profile][hat]['allow']['path'] = hasher()
+            profile_data[profile][hat]['allow']['dbus'] = list()
             # Save the initial comment
             if initial_comment:
                 profile_data[profile][hat]['initial_comment'] = initial_comment
@@ -2926,6 +2930,29 @@ def parse_profile_data(data, file, do_in
                 profile_data[profile][hat][allow]['netdomain']['rule']['all'] = True
                 profile_data[profile][hat][allow]['netdomain']['audit']['all'] = audit  # True
 
+        elif RE_PROFILE_DBUS.search(line):
+            matches = RE_PROFILE_DBUS.search(line).groups()
+
+            if not profile:
+                raise AppArmorException(_('Syntax Error: Unexpected dbus entry found in file: %s line: %s') % (file, lineno + 1))
+
+            audit = False
+            if matches[0]:
+                audit = True
+            allow = 'allow'
+            if matches[1] and matches[1].strip() == 'deny':
+                allow = 'deny'
+            dbus = matches[2]
+
+            #parse_dbus_rule(profile_data[profile], dbus, audit, allow)
+            dbus_rule = parse_dbus_rule(dbus)
+            dbus_rule.audit = audit
+            dbus_rule.deny = (allow == 'deny')
+
+            dbus_rules = profile_data[profile][hat][allow].get('dbus', list())
+            dbus_rules.append(dbus_rule)
+            profile_data[profile][hat][allow]['dbus'] = dbus_rules
+
         elif RE_PROFILE_CHANGE_HAT.search(line):
             matches = RE_PROFILE_CHANGE_HAT.search(line).groups()
 
@@ -2998,6 +3025,21 @@ def parse_profile_data(data, file, do_in
 
     return profile_data
 
+# RE_DBUS_ENTRY = re.compile('^dbus\s*()?,\s*$')
+#   use stuff like '(?P<action>(send|write|w|receive|read|r|rw))'
+
+def parse_dbus_rule(line):
+    # XXX Do real parsing here
+    return aarules.Raw_DBUS_Rule(line)
+
+    #matches = RE_DBUS_ENTRY.search(line).groups()
+    #if len(matches) == 1:
+        # XXX warn?
+        # matched nothing
+    #    print('no matches')
+    #    return aarules.DBUS_Rule()
+    #print(line)
+
 def separate_vars(vs):
     """Returns a list of all the values for a variable"""
     data = []
@@ -3188,6 +3230,24 @@ def write_netdomain(prof_data, depth):
     data += write_net_rules(prof_data, depth, 'allow')
     return data
 
+def write_dbus_rules(prof_data, depth, allow):
+    pre = '  ' * depth
+    data = []
+
+    # no dbus rules, so return
+    if not prof_data[allow].get('dbus', False):
+        return data
+
+    for dbus_rule in prof_data[allow]['dbus']:
+        data.append('%s%s' % (pre, dbus_rule.serialize()))
+    data.append('')
+    return data
+
+def write_dbus(prof_data, depth):
+    data = write_dbus_rules(prof_data, depth, 'deny')
+    data += write_net_rules(prof_data, depth, 'allow')
+    return data
+
 def write_link_rules(prof_data, depth, allow):
     pre = '  ' * depth
     data = []
@@ -3280,6 +3340,7 @@ def write_rules(prof_data, depth):
     data += write_rlimits(prof_data, depth)
     data += write_capabilities(prof_data, depth)
     data += write_netdomain(prof_data, depth)
+    data += write_dbus(prof_data, depth)
     data += write_links(prof_data, depth)
     data += write_paths(prof_data, depth)
     data += write_change_profile(prof_data, depth)
@@ -3427,6 +3488,7 @@ def serialize_profile_from_old_profile(p
                          'rlimit': write_rlimits,
                          'capability': write_capabilities,
                          'netdomain': write_netdomain,
+                         'dbus': write_dbus,
                          'link': write_links,
                          'path': write_paths,
                          'change_profile': write_change_profile,
@@ -3438,6 +3500,7 @@ def serialize_profile_from_old_profile(p
                     'rlimit': False,
                     'capability': False,
                     'netdomain': False,
+                    'dbus': False,
                     'link': False,
                     'path': False,
                     'change_profile': False,
@@ -3516,6 +3579,7 @@ def serialize_profile_from_old_profile(p
                     data += write_rlimits(write_prof_data, depth)
                     data += write_capabilities(write_prof_data[name], depth)
                     data += write_netdomain(write_prof_data[name], depth)
+                    data += write_dbus(write_prof_data[name], depth)
                     data += write_links(write_prof_data[name], depth)
                     data += write_paths(write_prof_data[name], depth)
                     data += write_change_profile(write_prof_data[name], depth)
Index: b/utils/apparmor/rules.py
===================================================================
--- /dev/null
+++ b/utils/apparmor/rules.py
@@ -0,0 +1,57 @@
+# ------------------------------------------------------------------
+#
+#    Copyright (C) 2014 Canonical Ltd.
+#
+#    This program is free software; you can redistribute it and/or
+#    modify it under the terms of version 2 of the GNU General Public
+#    License published by the Free Software Foundation.
+#
+# ------------------------------------------------------------------
+
+class DBUS_Rule(object):
+    actions = set()
+    busses = set()
+    names = set()
+    paths = set()
+    interfaces = set()
+    members = set()
+    peer_names = set()
+    peer_labels = set()
+
+    audit = False
+    deny = False
+
+    def __init__(self, actions=[], busses=[], names=[], paths=[], interfaces=[],
+                 members=[], peer_names=[], peer_labels=[]):
+        self.actions = set(actions)
+        self.busses = set(busses)
+        self.names = set(names)
+        self.paths = set(paths)
+        self.interfaces = set(interfaces)
+        self.members = set(members)
+        self.peer_name = set(peer_names)
+        self.peer_labels = set(peer_labels)
+
+    def serialize(self):
+        out = "%s%s%s" % ('audit ' if self.audit else '',
+                          'deny '  if self.deny else '',
+                          'dbus')
+        if len(self.actions) > 0:
+            if len(self.actions) == 1:
+                out += ' %s' % self.actions[0]
+            else:
+                out += ' (%s)' % (', '.join(self.actions))
+        out += ','
+        return out
+
+class Raw_DBUS_Rule(object):
+    audit = False
+    deny = False
+
+    def __init__(self, rule):
+        self.rule = rule
+
+    def serialize(self):
+        return "%s%s%s" % ('audit ' if self.audit else '',
+                           'deny '  if self.deny else '',
+                           self.rule)
Index: b/utils/test/test-dbus_parse.py
===================================================================
--- /dev/null
+++ b/utils/test/test-dbus_parse.py
@@ -0,0 +1,30 @@
+#! /usr/bin/env python
+# ------------------------------------------------------------------
+#
+#    Copyright (C) 2014 Canonical Ltd.
+#
+#    This program is free software; you can redistribute it and/or
+#    modify it under the terms of version 2 of the GNU General Public
+#    License published by the Free Software Foundation.
+#
+# ------------------------------------------------------------------
+
+import apparmor.aa as aa
+import unittest
+
+class AAParseDBUSTest(unittest.TestCase):
+
+    def test_parse_plain_dbus_rule(self):
+        dstring = 'dbus,'
+        dbus = aa.parse_dbus_rule(dstring)
+        self.assertEqual(dstring, dbus.serialize(),
+                'dbus object returned "%s", expected "%s"' % (dbus.serialize(), dstring))
+
+    def test_parse_dbus_simple_send_rule(self):
+        dstring = 'dbus send,'
+        dbus = aa.parse_dbus_rule(dstring)
+        self.assertEqual(dstring, dbus.serialize(),
+                'dbus object returned "%s", expected "%s"' % (dbus.serialize(), dstring))
+
+if __name__ == '__main__':
+    unittest.main()
Index: b/utils/test/Makefile
===================================================================
--- a/utils/test/Makefile
+++ b/utils/test/Makefile
@@ -38,4 +38,4 @@ ifndef VERBOSE
 .SILENT: check
 endif
 check:
-	$(foreach test, $(wildcard test-*.py), $(call pyalldo, $(test)))
+	export PYTHONPATH=.. ; $(foreach test, $(wildcard test-*.py), $(call pyalldo, $(test)))




More information about the AppArmor mailing list