[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