[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