[apparmor] [PATCH 3/3] utils: Basic support for pivot_root rules

Steve Beattie steve at nxnw.org
Fri Apr 4 18:17:17 UTC 2014


On Thu, Apr 03, 2014 at 02:56:01PM -0500, Tyler Hicks wrote:
> Bug: https://bugs.launchpad.net/bugs/1298678
> 
> This patch does bare bones parsing of pivot_root rules and stores the raw
> strings for writing them out later. It is meant to be a simple change to
> prevent aa.py from emitting a traceback when encountering pivot_root rules.
> 
> Signed-off-by: Tyler Hicks <tyhicks at canonical.com>

A meta question: do we see the pivot_root policy statements as a
significantly different class of rules from the rest of the mount
rules to be worth handling them separately? (I'm unfortunately not
as familiar with the intricacies of those classes of rules.)

> ---
>  utils/apparmor/aa.py                | 49 +++++++++++++++++++++++
>  utils/apparmor/rules.py             | 12 ++++++
>  utils/test/test-pivot_root_parse.py | 36 +++++++++++++++++
>  utils/test/test-regex_matches.py    | 79 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 176 insertions(+)
>  create mode 100644 utils/test/test-pivot_root_parse.py
> 
> diff --git a/utils/apparmor/aa.py b/utils/apparmor/aa.py
> index 8c1e374..a2971c3 100644
> --- a/utils/apparmor/aa.py
> +++ b/utils/apparmor/aa.py
> @@ -2626,6 +2626,7 @@ RE_PROFILE_DBUS = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?(dbus[^#]*\s*,)\
>  RE_PROFILE_MOUNT = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?((mount|remount|umount)[^#]*\s*,)\s*(#.*)?$')
>  RE_PROFILE_SIGNAL = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?((signal)[^#]*\s*,)\s*(#.*)?$')
>  RE_PROFILE_PTRACE = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?((ptrace)[^#]*\s*,)\s*(#.*)?$')
> +RE_PROFILE_PIVOT_ROOT = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?((pivot_root)[^#]*\s*,)\s*(#.*)?$')
>  
>  # match anything that's not " or #, or matching quotes with anything except quotes inside
>  __re_no_or_quoted_hash = '([^#"]|"[^"]*")*'
> @@ -2706,6 +2707,7 @@ def parse_profile_data(data, file, do_include):
>              profile_data[profile][hat]['allow']['mount'] = list()
>              profile_data[profile][hat]['allow']['signal'] = list()
>              profile_data[profile][hat]['allow']['ptrace'] = list()
> +            profile_data[profile][hat]['allow']['pivot_root'] = list()
>              # Save the initial comment
>              if initial_comment:
>                  profile_data[profile][hat]['initial_comment'] = initial_comment
> @@ -3110,6 +3112,28 @@ def parse_profile_data(data, file, do_include):
>              ptrace_rules.append(ptrace_rule)
>              profile_data[profile][hat][allow]['ptrace'] = ptrace_rules
>  
> +        elif RE_PROFILE_PIVOT_ROOT.search(line):
> +            matches = RE_PROFILE_PIVOT_ROOT.search(line).groups()
> +
> +            if not profile:
> +                raise AppArmorException(_('Syntax Error: Unexpected ptrace entry found in file: %s line: %s') % (file, lineno + 1))

s/ptrace/pivot_root/ here

> +
> +            audit = False
> +            if matches[0]:
> +                audit = True
> +            allow = 'allow'
> +            if matches[1] and matches[1].strip() == 'deny':
> +                allow = 'deny'
> +            pivot_root = matches[2].strip()
> +
> +            pivot_root_rule = parse_pivot_root_rule(pivot_root)
> +            pivot_root_rule.audit = audit
> +            pivot_root_rule.deny = (allow == 'deny')
> +
> +            pivot_root_rules = profile_data[profile][hat][allow].get('pivot_root', list())
> +            pivot_root_rules.append(pivot_root_rule)
> +            profile_data[profile][hat][allow]['pivot_root'] = pivot_root_rules
> +
>          elif RE_PROFILE_CHANGE_HAT.search(line):
>              matches = RE_PROFILE_CHANGE_HAT.search(line).groups()
>  
> @@ -3216,6 +3240,10 @@ def parse_ptrace_rule(line):
>      # XXX Do real parsing here
>      return aarules.Raw_Ptrace_Rule(line)
>  
> +def parse_pivot_root_rule(line):
> +    # XXX Do real parsing here
> +    return aarules.Raw_Pivot_Root_Rule(line)
> +
>  def separate_vars(vs):
>      """Returns a list of all the values for a variable"""
>      data = []
> @@ -3481,6 +3509,24 @@ def write_ptrace(prof_data, depth):
>      data += write_ptrace_rules(prof_data, depth, 'allow')
>      return data
>  
> +def write_pivot_root_rules(prof_data, depth, allow):
> +    pre = '  ' * depth
> +    data = []
> +
> +    # no pivot_root rules, so return
> +    if not prof_data[allow].get('pivot_root', False):
> +        return data
> +
> +    for pivot_root_rule in prof_data[allow]['pivot_root']:
> +        data.append('%s%s' % (pre, pivot_root_rule.serialize()))
> +    data.append('')
> +    return data
> +
> +def write_pivot_root(prof_data, depth):
> +    data = write_pivot_root_rules(prof_data, depth, 'deny')
> +    data += write_pivot_root_rules(prof_data, depth, 'allow')
> +    return data
> +
>  def write_link_rules(prof_data, depth, allow):
>      pre = '  ' * depth
>      data = []
> @@ -3589,6 +3635,7 @@ def write_rules(prof_data, depth):
>      data += write_mount(prof_data, depth)
>      data += write_signal(prof_data, depth)
>      data += write_ptrace(prof_data, depth)
> +    data += write_pivot_root(prof_data, depth)
>      data += write_links(prof_data, depth)
>      data += write_paths(prof_data, depth)
>      data += write_change_profile(prof_data, depth)
> @@ -3740,6 +3787,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
>                           'mount': write_mount,
>                           'signal': write_signal,
>                           'ptrace': write_ptrace,
> +                         'pivot_root': write_pivot_root,
>                           'link': write_links,
>                           'path': write_paths,
>                           'change_profile': write_change_profile,
> @@ -3834,6 +3882,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
>                      data += write_mount(write_prof_data[name], depth)
>                      data += write_signal(write_prof_data[name], depth)
>                      data += write_ptrace(write_prof_data[name], depth)
> +                    data += write_pivot_root(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)
> diff --git a/utils/apparmor/rules.py b/utils/apparmor/rules.py
> index 5b93e1e..36640f3 100644
> --- a/utils/apparmor/rules.py
> +++ b/utils/apparmor/rules.py
> @@ -91,3 +91,15 @@ class Raw_Ptrace_Rule(object):
>          return "%s%s%s" % ('audit ' if self.audit else '',
>                             'deny '  if self.deny else '',
>                             self.rule)
> +
> +class Raw_Pivot_Root_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)
> diff --git a/utils/test/test-pivot_root_parse.py b/utils/test/test-pivot_root_parse.py
> new file mode 100644
> index 0000000..7b40f71
> --- /dev/null
> +++ b/utils/test/test-pivot_root_parse.py
> @@ -0,0 +1,36 @@
> +#! /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 AAParsePivotRootTest(unittest.TestCase):
> +
> +    def _test_parse_pivot_root_rule(self, rule):
> +        pivot_root = aa.parse_pivot_root_rule(rule)
> +        print(pivot_root.serialize())

Same nit as before here.

> +        self.assertEqual(rule, pivot_root.serialize(),
> +                'pivot_root object returned "%s", expected "%s"' % (pivot_root.serialize(), rule))
> +
> +    def test_parse_plain_pivot_root_rule(self):
> +        self._test_parse_pivot_root_rule('pivot_root,')
> +
> +    def test_parse_old_pivot_root_rule(self):
> +        self._test_parse_pivot_root_rule('pivot_root /old,')
> +
> +    def test_parse_new_pivot_root_rule(self):
> +        self._test_parse_pivot_root_rule('pivot_root /old /new,')
> +
> +    def test_parse_child_pivot_root_rule(self):
> +        self._test_parse_pivot_root_rule('pivot_root /old /new -> /usr/bin/child,')
> +
> +if __name__ == '__main__':
> +    unittest.main()

-- 
Steve Beattie
<sbeattie at ubuntu.com>
http://NxNW.org/~steve/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20140404/5411a7c3/attachment.pgp>


More information about the AppArmor mailing list