2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-31 14:25:52 +00:00

utils: Add very limited support for mount rules

Bug: https://bugs.launchpad.net/bugs/1294825

This patch is inspired by sbeattie's patch to add limited dbus rule
support. It adds does very dumb parsing of mount rules. Basically, it
stores mount, remount, and umount rules as raw strings wrapped in a
class.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
Acked-by: Christian Boltz <apparmor@cboltz.de>
This commit is contained in:
Tyler Hicks
2014-03-20 14:25:42 -05:00
parent 5dce40c97f
commit e5d9d541f6
3 changed files with 131 additions and 0 deletions

View File

@@ -2616,6 +2616,7 @@ RE_PROFILE_HAT_DEF = re.compile('^\s*\^(\"??.+?\"??)\s+((flags=)?\((.+)\)\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*,)\s*(#.*)?$')
RE_PROFILE_MOUNT = re.compile('^\s*(audit\s+)?(allow\s+|deny\s+)?((mount|remount|umount)[^#]*\s*,)\s*(#.*)?$')
# match anything that's not " or #, or matching quotes with anything except quotes inside
__re_no_or_quoted_hash = '([^#"]|"[^"]*")*'
@@ -2693,6 +2694,7 @@ def parse_profile_data(data, file, do_include):
profile_data[profile][hat]['allow']['netdomain'] = hasher()
profile_data[profile][hat]['allow']['path'] = hasher()
profile_data[profile][hat]['allow']['dbus'] = list()
profile_data[profile][hat]['allow']['mount'] = list()
# Save the initial comment
if initial_comment:
profile_data[profile][hat]['initial_comment'] = initial_comment
@@ -2966,6 +2968,28 @@ def parse_profile_data(data, file, do_include):
dbus_rules.append(dbus_rule)
profile_data[profile][hat][allow]['dbus'] = dbus_rules
elif RE_PROFILE_MOUNT.search(line):
matches = RE_PROFILE_MOUNT.search(line).groups()
if not profile:
raise AppArmorException(_('Syntax Error: Unexpected mount 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'
mount = matches[2]
mount_rule = parse_mount_rule(mount)
mount_rule.audit = audit
mount_rule.deny = (allow == 'deny')
mount_rules = profile_data[profile][hat][allow].get('mount', list())
mount_rules.append(mount_rule)
profile_data[profile][hat][allow]['mount'] = mount_rules
elif RE_PROFILE_CHANGE_HAT.search(line):
matches = RE_PROFILE_CHANGE_HAT.search(line).groups()
@@ -3060,6 +3084,10 @@ def parse_dbus_rule(line):
# return aarules.DBUS_Rule()
#print(line)
def parse_mount_rule(line):
# XXX Do real parsing here
return aarules.Raw_Mount_Rule(line)
def separate_vars(vs):
"""Returns a list of all the values for a variable"""
data = []
@@ -3268,6 +3296,24 @@ def write_dbus(prof_data, depth):
data += write_dbus_rules(prof_data, depth, 'allow')
return data
def write_mount_rules(prof_data, depth, allow):
pre = ' ' * depth
data = []
# no mount rules, so return
if not prof_data[allow].get('mount', False):
return data
for mount_rule in prof_data[allow]['mount']:
data.append('%s%s' % (pre, mount_rule.serialize()))
data.append('')
return data
def write_mount(prof_data, depth):
data = write_mount_rules(prof_data, depth, 'deny')
data += write_mount_rules(prof_data, depth, 'allow')
return data
def write_link_rules(prof_data, depth, allow):
pre = ' ' * depth
data = []
@@ -3361,6 +3407,7 @@ def write_rules(prof_data, depth):
data += write_capabilities(prof_data, depth)
data += write_netdomain(prof_data, depth)
data += write_dbus(prof_data, depth)
data += write_mount(prof_data, depth)
data += write_links(prof_data, depth)
data += write_paths(prof_data, depth)
data += write_change_profile(prof_data, depth)
@@ -3509,6 +3556,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
'capability': write_capabilities,
'netdomain': write_netdomain,
'dbus': write_dbus,
'mount': write_mount,
'link': write_links,
'path': write_paths,
'change_profile': write_change_profile,
@@ -3600,6 +3648,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
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_mount(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)

View File

@@ -55,3 +55,15 @@ class Raw_DBUS_Rule(object):
return "%s%s%s" % ('audit ' if self.audit else '',
'deny ' if self.deny else '',
self.rule)
class Raw_Mount_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)

View File

@@ -0,0 +1,70 @@
#! /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 AAParseMountTest(unittest.TestCase):
def test_parse_plain_mount_rule(self):
rule = 'mount,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
def test_parse_ro_mount(self):
rule = 'mount -o ro,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
def test_parse_rw_mount_with_mount_points(self):
rule = 'mount -o rw /dev/sdb1 -> /mnt/external,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
class AAParseRemountTest(unittest.TestCase):
def test_parse_plain_remount_rule(self):
rule = 'remount,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
def test_parse_ro_remount(self):
rule = 'remount -o ro,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
def test_parse_ro_remount_with_mount_point(self):
rule = 'remount -o ro /,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
class AAParseUmountTest(unittest.TestCase):
def test_parse_plain_umount_rule(self):
rule = 'umount,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
def test_parse_umount_with_mount_point(self):
rule = 'umount /mnt/external,'
mount = aa.parse_mount_rule(rule)
self.assertEqual(rule, mount.serialize(),
'mount object returned "%s", expected "%s"' % (mount.serialize(), rule))
if __name__ == '__main__':
unittest.main()