[apparmor] [patch 10/11] utils: add very limited dbus rule support
Seth Arnold
seth.arnold at canonical.com
Thu Mar 6 08:08:23 UTC 2014
On Wed, Mar 05, 2014 at 05:44:44PM -0800, Steve Beattie wrote:
> 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>
Nice!
Acked-by: Seth Arnold <seth.arnold at canonical.com>
> ---
> 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)))
>
>
> --
> AppArmor mailing list
> AppArmor at lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20140306/db1d45ee/attachment.pgp>
More information about the AppArmor
mailing list