mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 14:55:10 +00:00
Compare commits
30 Commits
v4.0.0-bet
...
v4.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
353ba896d4 | ||
|
c13007f7fc | ||
|
88a420853e | ||
|
2db41acd1b | ||
|
a10d9044b8 | ||
|
b53441a689 | ||
|
7452f34279 | ||
|
aedb8a5b00 | ||
|
6695944c2c | ||
|
01090dcf1b | ||
|
3ea2bfec56 | ||
|
3d1a867c0a | ||
|
dfb02cbd93 | ||
|
90f056c1c6 | ||
|
0daf3e8c9b | ||
|
a86c1bd45a | ||
|
8f4073ecd9 | ||
|
440be71c12 | ||
|
8d21f01924 | ||
|
4e546291a5 | ||
|
8c026077d6 | ||
|
5e4c4a0cb3 | ||
|
9c27a7c435 | ||
|
a367c07437 | ||
|
2200013088 | ||
|
d5afc33c40 | ||
|
517e7c96c8 | ||
|
a7cd59819e | ||
|
e7f5ee3271 | ||
|
da75b1c8d8 |
@@ -67,10 +67,10 @@ to syslog.
|
|||||||
References
|
References
|
||||||
----------
|
----------
|
||||||
Project webpage:
|
Project webpage:
|
||||||
http://developer.novell.com/wiki/index.php/Novell_AppArmor
|
https://apparmor.net/
|
||||||
|
|
||||||
To provide feedback or ask questions please contact the
|
To provide feedback or ask questions please contact the
|
||||||
apparmor-dev@forge.novell.com mail list. This is the development list
|
apparmor@lists.ubuntu.com mail list. This is the development list
|
||||||
for the AppArmor team.
|
for the AppArmor team.
|
||||||
|
|
||||||
See also: change_hat(3), and the Linux-PAM online documentation at
|
See also: change_hat(3), and the Linux-PAM online documentation at
|
||||||
|
@@ -188,10 +188,9 @@ parent context.
|
|||||||
8. Feedback/Resources
|
8. Feedback/Resources
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
To provide feedback or ask questions please contact the
|
Project webpage:
|
||||||
apparmor-dev@forge.novell.com mail list. This is the development list for the
|
https://apparmor.net/
|
||||||
AppArmor team.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To provide feedback or ask questions please contact the
|
||||||
|
apparmor@lists.ubuntu.com mail list. This is the development list
|
||||||
|
for the AppArmor team.
|
||||||
|
@@ -188,10 +188,9 @@ parent context.
|
|||||||
8. Feedback/Resources
|
8. Feedback/Resources
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
To provide feedback or ask questions please contact the
|
Project webpage:
|
||||||
apparmor-dev@forge.novell.com mail list. This is the development list for the
|
https://apparmor.net/
|
||||||
AppArmor team.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To provide feedback or ask questions please contact the
|
||||||
|
apparmor@lists.ubuntu.com mail list. This is the development list
|
||||||
|
for the AppArmor team.
|
||||||
|
@@ -1 +1 @@
|
|||||||
4.0.0~beta1
|
4.0.0~beta2
|
||||||
|
@@ -92,6 +92,7 @@ if test "$ac_cv_prog_cc_c99" = "no"; then
|
|||||||
AC_MSG_ERROR([C99 mode is required to build libapparmor])
|
AC_MSG_ERROR([C99 mode is required to build libapparmor])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
m4_ifndef([AX_CHECK_COMPILE_FLAG], [AC_MSG_ERROR(['autoconf-archive' missing])])
|
||||||
EXTRA_CFLAGS="-Wall $(EXTRA_WARNINGS) -fPIC"
|
EXTRA_CFLAGS="-Wall $(EXTRA_WARNINGS) -fPIC"
|
||||||
AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror])
|
AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror])
|
||||||
AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes],
|
AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes],
|
||||||
|
@@ -2037,8 +2037,6 @@ An example AppArmor profile:
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item F</etc/init.d/boot.apparmor>
|
|
||||||
|
|
||||||
=item F</etc/apparmor.d/>
|
=item F</etc/apparmor.d/>
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
@@ -36,12 +36,11 @@ of resources. AppArmor's unique security model is to bind access control
|
|||||||
attributes to programs rather than to users.
|
attributes to programs rather than to users.
|
||||||
|
|
||||||
AppArmor confinement is provided via I<profiles> loaded into the kernel
|
AppArmor confinement is provided via I<profiles> loaded into the kernel
|
||||||
via apparmor_parser(8), typically through the F</etc/init.d/apparmor>
|
via apparmor_parser(8), typically through the F<apparmor.service>
|
||||||
SysV initscript, which is used like this:
|
systemd unit, which is used like this:
|
||||||
|
|
||||||
# /etc/init.d/apparmor start
|
# systemctl start apparmor
|
||||||
# /etc/init.d/apparmor stop
|
# systemctl reload apparmor
|
||||||
# /etc/init.d/apparmor restart
|
|
||||||
|
|
||||||
AppArmor can operate in two modes: I<enforcement>, and I<complain or learning>:
|
AppArmor can operate in two modes: I<enforcement>, and I<complain or learning>:
|
||||||
|
|
||||||
@@ -273,11 +272,9 @@ Else, if auditd is running, see auditd(8) and auditd.conf(5).
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item F</etc/init.d/apparmor>
|
|
||||||
|
|
||||||
=item F</etc/apparmor.d/>
|
=item F</etc/apparmor.d/>
|
||||||
|
|
||||||
=item F</var/lib/apparmor/>
|
=item F</var/cache/apparmor/>
|
||||||
|
|
||||||
=item F</var/log/audit/audit.log>
|
=item F</var/log/audit/audit.log>
|
||||||
|
|
||||||
|
@@ -234,6 +234,7 @@ struct mnt_keyword_table {
|
|||||||
unsigned int clear;
|
unsigned int clear;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// keep in sync with utils/apparmor/rule/mount.py flags_keywords
|
||||||
static struct mnt_keyword_table mnt_opts_table[] = {
|
static struct mnt_keyword_table mnt_opts_table[] = {
|
||||||
{"ro", MS_RDONLY, 0},
|
{"ro", MS_RDONLY, 0},
|
||||||
{"r", MS_RDONLY, 0},
|
{"r", MS_RDONLY, 0},
|
||||||
|
@@ -612,10 +612,10 @@ bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mas
|
|||||||
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!features_supports_inet) {
|
if (!features_supports_inet || (family != AF_INET && family != AF_INET6)) {
|
||||||
buf = buffer.str();
|
buf = buffer.str();
|
||||||
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(AA_VALID_NET_PERMS),
|
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms),
|
||||||
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(AA_VALID_NET_PERMS) : 0,
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
||||||
parseopts))
|
parseopts))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
@@ -39,7 +39,7 @@ from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, ruletyp
|
|||||||
from apparmor.regex import (
|
from apparmor.regex import (
|
||||||
RE_HAS_COMMENT_SPLIT, RE_PROFILE_CHANGE_HAT, RE_PROFILE_CONDITIONAL,
|
RE_HAS_COMMENT_SPLIT, RE_PROFILE_CHANGE_HAT, RE_PROFILE_CONDITIONAL,
|
||||||
RE_PROFILE_CONDITIONAL_BOOLEAN, RE_PROFILE_CONDITIONAL_VARIABLE, RE_PROFILE_END,
|
RE_PROFILE_CONDITIONAL_BOOLEAN, RE_PROFILE_CONDITIONAL_VARIABLE, RE_PROFILE_END,
|
||||||
RE_PROFILE_HAT_DEF, RE_PROFILE_MOUNT, RE_PROFILE_PIVOT_ROOT, RE_PROFILE_START,
|
RE_PROFILE_HAT_DEF, RE_PROFILE_PIVOT_ROOT, RE_PROFILE_START,
|
||||||
RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, parse_profile_start_line, re_match_include)
|
RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, parse_profile_start_line, re_match_include)
|
||||||
from apparmor.rule.abi import AbiRule
|
from apparmor.rule.abi import AbiRule
|
||||||
from apparmor.rule.capability import CapabilityRule
|
from apparmor.rule.capability import CapabilityRule
|
||||||
@@ -1995,29 +1995,6 @@ def parse_profile_data(data, file, do_include, in_preamble):
|
|||||||
# Conditional Boolean defined
|
# Conditional Boolean defined
|
||||||
pass
|
pass
|
||||||
|
|
||||||
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: %(file)s line: %(line)s')
|
|
||||||
% {'file': file, 'line': 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[profname][allow].get('mount', [])
|
|
||||||
mount_rules.append(mount_rule)
|
|
||||||
profile_data[profname][allow]['mount'] = mount_rules
|
|
||||||
|
|
||||||
elif RE_PROFILE_PIVOT_ROOT.search(line):
|
elif RE_PROFILE_PIVOT_ROOT.search(line):
|
||||||
matches = RE_PROFILE_PIVOT_ROOT.search(line).groups()
|
matches = RE_PROFILE_PIVOT_ROOT.search(line).groups()
|
||||||
|
|
||||||
@@ -2202,11 +2179,6 @@ def split_to_merged(profile_data):
|
|||||||
return merged
|
return merged
|
||||||
|
|
||||||
|
|
||||||
def parse_mount_rule(line):
|
|
||||||
# XXX Do real parsing here
|
|
||||||
return aarules.Raw_Mount_Rule(line)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_pivot_root_rule(line):
|
def parse_pivot_root_rule(line):
|
||||||
# XXX Do real parsing here
|
# XXX Do real parsing here
|
||||||
return aarules.Raw_Pivot_Root_Rule(line)
|
return aarules.Raw_Pivot_Root_Rule(line)
|
||||||
|
@@ -16,7 +16,7 @@ import re
|
|||||||
from apparmor.common import AppArmorBug, AppArmorException
|
from apparmor.common import AppArmorBug, AppArmorException
|
||||||
|
|
||||||
from apparmor.regex import RE_PROFILE_MOUNT, RE_PROFILE_PATH_OR_VAR, strip_parenthesis
|
from apparmor.regex import RE_PROFILE_MOUNT, RE_PROFILE_PATH_OR_VAR, strip_parenthesis
|
||||||
#from apparmor.rule import AARE
|
# from apparmor.rule import AARE
|
||||||
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers, logprof_value_or_all, check_and_split_list
|
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers, logprof_value_or_all, check_and_split_list
|
||||||
|
|
||||||
from apparmor.translations import init_translation
|
from apparmor.translations import init_translation
|
||||||
@@ -33,40 +33,44 @@ valid_fs = [
|
|||||||
'sysfs', 'tmpfs', 'bdevfs', 'procfs', 'cgroup', 'cgroup2', 'cpuset', 'devtmpfs', 'configfs', 'debugfs', 'tracefs',
|
'sysfs', 'tmpfs', 'bdevfs', 'procfs', 'cgroup', 'cgroup2', 'cpuset', 'devtmpfs', 'configfs', 'debugfs', 'tracefs',
|
||||||
'securityfs', 'sockfs', 'bpf', 'npipefs', 'ramfs', 'hugetlbfs', 'devpts', 'ext3', 'ext2', 'ext4', 'squashfs',
|
'securityfs', 'sockfs', 'bpf', 'npipefs', 'ramfs', 'hugetlbfs', 'devpts', 'ext3', 'ext2', 'ext4', 'squashfs',
|
||||||
'vfat', 'ecryptfs', 'fuseblk', 'fuse', 'fusectl', 'efivarfs', 'mqueue', 'store', 'autofs', 'binfmt_misc', 'overlay',
|
'vfat', 'ecryptfs', 'fuseblk', 'fuse', 'fusectl', 'efivarfs', 'mqueue', 'store', 'autofs', 'binfmt_misc', 'overlay',
|
||||||
'none', 'bdev', 'proc', 'pipefs', 'pstore', 'brtfs', 'xfs',
|
'none', 'bdev', 'proc', 'pipefs', 'pstore', 'btrfs', 'xfs', '9p',
|
||||||
]
|
]
|
||||||
flags_keywords = [
|
flags_keywords = [
|
||||||
'ro', 'rw', 'nosuid', 'suid', 'nodev', 'dev', 'noexec', 'exec', 'sync', 'async', 'remount', 'mand', 'nomand',
|
# keep in sync with parser/mount.cc mnt_opts_table!
|
||||||
'dirsync', 'noatime', 'atime', 'nodiratime', 'diratime', 'bind', 'rbind', 'move', 'verbose', 'silent', 'loud',
|
'ro', 'r', 'read-only', 'rw', 'w', 'suid', 'nosuid', 'dev', 'nodev', 'exec', 'noexec', 'sync', 'async', 'remount',
|
||||||
'acl', 'noacl', 'unbindable', 'runbindable', 'private', 'rprivate', 'slave', 'rslave', 'shared', 'rshared',
|
'mand', 'nomand', 'dirsync', 'symfollow', 'nosymfollow', 'atime', 'noatime', 'diratime', 'nodiratime', 'bind', 'B',
|
||||||
'relatime', 'norelatime', 'iversion', 'noiversion', 'strictatime', 'nostrictatime', 'lazytime', 'nolazytime',
|
'move', 'M', 'rbind', 'R', 'verbose', 'silent', 'loud', 'acl', 'noacl', 'unbindable', 'make-unbindable', 'runbindable',
|
||||||
'nouser', 'user', 'symfollow', 'nosymfollow', '([A-Za-z0-9]|AARE)', # TODO: handle AARE
|
'make-runbindable', 'private', 'make-private', 'rprivate', 'make-rprivate', 'slave', 'make-slave', 'rslave', 'make-rslave',
|
||||||
|
'shared', 'make-shared', 'rshared', 'make-rshared', 'relatime', 'norelatime', 'iversion', 'noiversion', 'strictatime',
|
||||||
|
'nostrictatime', 'lazytime', 'nolazytime', 'user', 'nouser',
|
||||||
|
'([A-Za-z0-9]|AARE)', # TODO: handle AARE
|
||||||
]
|
]
|
||||||
join_valid_flags = '|'.join(flags_keywords)
|
join_valid_flags = '|'.join(flags_keywords)
|
||||||
join_valid_fs = '|'.join(valid_fs)
|
join_valid_fs = '|'.join(valid_fs)
|
||||||
|
|
||||||
sep = r"\s*[\s,]\s*"
|
sep = r'\s*[\s,]\s*'
|
||||||
|
|
||||||
fs_type_pattern = r"\b(?P<fstype_or_vfstype>fstype|vfstype)\b\s*(?P<fstype_equals_or_in>=|in)\s*"\
|
fs_type_pattern = r'\b(?P<fstype_or_vfstype>fstype|vfstype)\b\s*(?P<fstype_equals_or_in>=|in)\s*'\
|
||||||
r"(?P<fstype>\(\s*(" + join_valid_fs + ")(" + sep + "(" + join_valid_fs + "))*\s*\)|"\
|
r'(?P<fstype>\(\s*(' + join_valid_fs + r')(' + sep + r'(' + join_valid_fs + r'))*\s*\)|'\
|
||||||
r"\{\s*(" + join_valid_fs + ")(" + sep + "(" + join_valid_fs + r"))*\s*\}|(\s*" + join_valid_fs + "))"\
|
r'\{\s*(' + join_valid_fs + r')(' + sep + r'(' + join_valid_fs + r'))*\s*\}|(\s*' + join_valid_fs + r'))'\
|
||||||
|
|
||||||
|
|
||||||
option_pattern = r"\s*(\boption(s?)\b\s*(?P<options_equals_or_in>=|in)\s*"\
|
option_pattern = r'\s*(\boption(s?)\b\s*(?P<options_equals_or_in>=|in)\s*'\
|
||||||
r"(?P<options>\(\s*(" + join_valid_flags + ")(" + sep + "(" + join_valid_flags + r"))*\s*\)|" \
|
r'(?P<options>\(\s*(' + join_valid_flags + r')(' + sep + r'(' + join_valid_flags + r'))*\s*\)|' \
|
||||||
"(\s*" + join_valid_flags + ")"\
|
r'(\s*' + join_valid_flags + r')'\
|
||||||
"))?"
|
r'))?'
|
||||||
mount_condition_pattern = rf"({fs_type_pattern})?\s*({option_pattern})?"
|
mount_condition_pattern = rf'({fs_type_pattern})?\s*({option_pattern})?'
|
||||||
|
|
||||||
# Source can either be
|
# Source can either be
|
||||||
# - A path : /foo
|
# - A path : /foo
|
||||||
# - A filesystem : sysfs (sudo mount -t tmpfs tmpfs /tmp/bar)
|
# - A filesystem : sysfs (sudo mount -t tmpfs tmpfs /tmp/bar)
|
||||||
# - Any label : mntlabel (sudo mount -t tmpfs mntlabel /tmp/bar)
|
# - Any label : mntlabel (sudo mount -t tmpfs mntlabel /tmp/bar)
|
||||||
source_fileglob_pattern = r"(\s*" + (RE_PROFILE_PATH_OR_VAR % "source_file")[:-1] + "|" + r"\w+" + "))"
|
source_fileglob_pattern = r'(\s*' + (RE_PROFILE_PATH_OR_VAR % 'source_file')[:-1] + r'|' + r'\w+' + r'))'
|
||||||
dest_fileglob_pattern = r"(\s*" + RE_PROFILE_PATH_OR_VAR % "dest_file" + ')'
|
dest_fileglob_pattern = r'(\s*' + RE_PROFILE_PATH_OR_VAR % 'dest_file' + r')'
|
||||||
|
|
||||||
|
RE_MOUNT_DETAILS = re.compile(r'^\s*' + mount_condition_pattern + rf'(\s+{source_fileglob_pattern})?' + rf'(\s+->\s+{dest_fileglob_pattern})?\s*' + r'$')
|
||||||
|
RE_UMOUNT_DETAILS = re.compile(r'^\s*' + mount_condition_pattern + rf'(\s+{dest_fileglob_pattern})?\s*' + r'$')
|
||||||
|
|
||||||
RE_MOUNT_DETAILS = re.compile(r"^\s*" + mount_condition_pattern + rf"(\s+{source_fileglob_pattern})?" + rf"(\s+->\s+{dest_fileglob_pattern})?\s*" +"$")
|
|
||||||
RE_UMOUNT_DETAILS = re.compile(r"^\s*" + mount_condition_pattern + rf"(\s+{dest_fileglob_pattern})?\s*" +"$")
|
|
||||||
|
|
||||||
class MountRule(BaseRule):
|
class MountRule(BaseRule):
|
||||||
'''Class to handle and store a single mount rule'''
|
'''Class to handle and store a single mount rule'''
|
||||||
@@ -91,22 +95,20 @@ class MountRule(BaseRule):
|
|||||||
self.operation = operation
|
self.operation = operation
|
||||||
|
|
||||||
self.fstype, self.all_fstype, unknown_items = check_and_split_list(fstype[1] if fstype != self.ALL else fstype, valid_fs, self.ALL, type(self).__name__, 'fstype')
|
self.fstype, self.all_fstype, unknown_items = check_and_split_list(fstype[1] if fstype != self.ALL else fstype, valid_fs, self.ALL, type(self).__name__, 'fstype')
|
||||||
|
if unknown_items:
|
||||||
|
raise AppArmorException(_('Passed unknown fstype keyword to %s: %s') % (type(self).__name__, ' '.join(unknown_items)))
|
||||||
self.is_fstype_equal = fstype[0] if not self.all_fstype else None
|
self.is_fstype_equal = fstype[0] if not self.all_fstype else None
|
||||||
|
|
||||||
self.options, self.all_options, unknown_items = check_and_split_list(options[1] if options != self.ALL else options, flags_keywords, self.ALL, type(self).__name__, 'options')
|
self.options, self.all_options, unknown_items = check_and_split_list(options[1] if options != self.ALL else options, flags_keywords, self.ALL, type(self).__name__, 'options')
|
||||||
|
if unknown_items:
|
||||||
|
raise AppArmorException(_('Passed unknown options keyword to %s: %s') % (type(self).__name__, ' '.join(unknown_items)))
|
||||||
self.is_options_equal = options[0] if not self.all_options else None
|
self.is_options_equal = options[0] if not self.all_options else None
|
||||||
|
|
||||||
if source != self.ALL and source[0].isalpha():
|
self.source, self.all_source = self._aare_or_all(source, 'source', is_path=False, log_event=log_event)
|
||||||
self.source = source
|
|
||||||
self.all_source = False
|
|
||||||
self.source_is_path = False
|
|
||||||
else:
|
|
||||||
self.source_is_path = True
|
|
||||||
self.source, self.all_source = self._aare_or_all(source, 'source', is_path=self.source_is_path, log_event=log_event)
|
|
||||||
|
|
||||||
if not self.all_fstype and self.is_fstype_equal != "=" and self.is_fstype_equal != "in":
|
if not self.all_fstype and self.is_fstype_equal not in ('=', 'in'):
|
||||||
raise AppArmorBug(f'Invalid is_fstype_equal : {self.is_fstype_equal}')
|
raise AppArmorBug(f'Invalid is_fstype_equal : {self.is_fstype_equal}')
|
||||||
if not self.all_options and self.is_options_equal != "=" and self.is_options_equal != "in":
|
if not self.all_options and self.is_options_equal not in ('=', 'in'):
|
||||||
raise AppArmorBug(f'Invalid is_options_equal : {self.is_options_equal}')
|
raise AppArmorBug(f'Invalid is_options_equal : {self.is_options_equal}')
|
||||||
if self.operation != 'mount' and not self.all_source:
|
if self.operation != 'mount' and not self.all_source:
|
||||||
raise AppArmorException(f'Operation {self.operation} cannot have a source')
|
raise AppArmorException(f'Operation {self.operation} cannot have a source')
|
||||||
@@ -119,7 +121,6 @@ class MountRule(BaseRule):
|
|||||||
|
|
||||||
self.can_glob = not self.all_source and not self.all_dest and not self.all_options
|
self.can_glob = not self.all_source and not self.all_dest and not self.all_options
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_instance(cls, raw_rule, matches):
|
def _create_instance(cls, raw_rule, matches):
|
||||||
'''parse raw_rule and return instance of this class'''
|
'''parse raw_rule and return instance of this class'''
|
||||||
@@ -132,7 +133,7 @@ class MountRule(BaseRule):
|
|||||||
if matches.group('details'):
|
if matches.group('details'):
|
||||||
rule_details = matches.group('details')
|
rule_details = matches.group('details')
|
||||||
|
|
||||||
if operation == "mount":
|
if operation == 'mount':
|
||||||
parsed = RE_MOUNT_DETAILS.search(rule_details)
|
parsed = RE_MOUNT_DETAILS.search(rule_details)
|
||||||
else:
|
else:
|
||||||
parsed = RE_UMOUNT_DETAILS.search(rule_details)
|
parsed = RE_UMOUNT_DETAILS.search(rule_details)
|
||||||
@@ -152,7 +153,7 @@ class MountRule(BaseRule):
|
|||||||
is_options_equal = r['options_equals_or_in']
|
is_options_equal = r['options_equals_or_in']
|
||||||
options = strip_parenthesis(r['options']).replace(',', ' ').split()
|
options = strip_parenthesis(r['options']).replace(',', ' ').split()
|
||||||
else:
|
else:
|
||||||
is_options_equal =None
|
is_options_equal = None
|
||||||
options = cls.ALL
|
options = cls.ALL
|
||||||
|
|
||||||
if operation == 'mount' and r['source_file'] is not None: # Umount cannot have a source
|
if operation == 'mount' and r['source_file'] is not None: # Umount cannot have a source
|
||||||
@@ -168,38 +169,42 @@ class MountRule(BaseRule):
|
|||||||
else:
|
else:
|
||||||
is_fstype_equal = None
|
is_fstype_equal = None
|
||||||
is_options_equal = None
|
is_options_equal = None
|
||||||
fstype = cls.ALL
|
fstype = cls.ALL
|
||||||
options = cls.ALL
|
options = cls.ALL
|
||||||
source = cls.ALL
|
source = cls.ALL
|
||||||
dest = cls.ALL
|
dest = cls.ALL
|
||||||
|
|
||||||
return cls(operation=operation, fstype=(is_fstype_equal, fstype), options=(is_options_equal, options), source=source, dest=dest, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment)
|
return cls(operation=operation, fstype=(is_fstype_equal, fstype), options=(is_options_equal, options), source=source, dest=dest, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment)
|
||||||
|
|
||||||
def get_clean(self, depth=0):
|
def get_clean(self, depth=0):
|
||||||
space = ' ' * depth
|
space = ' ' * depth
|
||||||
|
|
||||||
fstype = ' fstype%s(%s)' % (self.is_fstype_equal, ', '.join(sorted(self.fstype))) if not self.all_fstype else ''
|
fstype = ' fstype%s(%s)' % (wrap_in_with_spaces(self.is_fstype_equal), ', '.join(sorted(self.fstype))) if not self.all_fstype else ''
|
||||||
options = ' options%s(%s)' % (self.is_options_equal, ', '.join(sorted(self.options))) if not self.all_options else ''
|
options = ' options%s(%s)' % (wrap_in_with_spaces(self.is_options_equal), ', '.join(sorted(self.options))) if not self.all_options else ''
|
||||||
|
|
||||||
|
source = ''
|
||||||
|
dest = ''
|
||||||
|
|
||||||
if self.operation == 'mount':
|
if self.operation == 'mount':
|
||||||
return ('%s%s%s%s%s%s%s,%s' % ( self.modifiers_str(),
|
if not self.all_source:
|
||||||
space,
|
source = ' ' + str(self.source.regex)
|
||||||
self.operation,
|
|
||||||
fstype,
|
if not self.all_dest:
|
||||||
options,
|
dest = ' -> ' + str(self.dest.regex)
|
||||||
" " + str(self.source.regex) if not self.all_source else '',
|
|
||||||
" -> " + str(self.dest.regex) if not self.all_dest else '',
|
|
||||||
self.comment,
|
|
||||||
))
|
|
||||||
else:
|
else:
|
||||||
return ('%s%s%s%s%s%s,%s' % ( self.modifiers_str(),
|
if not self.all_dest:
|
||||||
space,
|
dest = ' ' + str(self.dest.regex)
|
||||||
self.operation,
|
|
||||||
fstype,
|
return ('%s%s%s%s%s%s%s,%s' % (self.modifiers_str(),
|
||||||
options,
|
space,
|
||||||
" " + str(self.dest.regex) if not self.all_dest else '',
|
self.operation,
|
||||||
self.comment,
|
fstype,
|
||||||
))
|
options,
|
||||||
|
source,
|
||||||
|
dest,
|
||||||
|
self.comment,
|
||||||
|
))
|
||||||
|
|
||||||
def _is_covered_localvars(self, other_rule):
|
def _is_covered_localvars(self, other_rule):
|
||||||
if self.operation != other_rule.operation:
|
if self.operation != other_rule.operation:
|
||||||
@@ -212,12 +217,7 @@ class MountRule(BaseRule):
|
|||||||
return False
|
return False
|
||||||
if not self._is_covered_list(self.options, self.all_options, other_rule.options, other_rule.all_options, 'options'):
|
if not self._is_covered_list(self.options, self.all_options, other_rule.options, other_rule.all_options, 'options'):
|
||||||
return False
|
return False
|
||||||
if not self.source_is_path and not other_rule.source_is_path:
|
if not self._is_covered_aare(self.source, self.all_source, other_rule.source, other_rule.all_source, 'source'):
|
||||||
if self.source != other_rule.source:
|
|
||||||
return False
|
|
||||||
elif self.source_is_path != other_rule.source_is_path:
|
|
||||||
return False
|
|
||||||
elif not self._is_covered_aare(self.source, self.all_source, other_rule.source, other_rule.all_source, 'source'):
|
|
||||||
return False
|
return False
|
||||||
if not self._is_covered_aare(self.dest, self.all_dest, other_rule.dest, other_rule.all_dest, 'dest'):
|
if not self._is_covered_aare(self.dest, self.all_dest, other_rule.dest, other_rule.all_dest, 'dest'):
|
||||||
return False
|
return False
|
||||||
@@ -233,12 +233,7 @@ class MountRule(BaseRule):
|
|||||||
return False
|
return False
|
||||||
if self.fstype != rule_obj.fstype or self.options != rule_obj.options:
|
if self.fstype != rule_obj.fstype or self.options != rule_obj.options:
|
||||||
return False
|
return False
|
||||||
if not self.source_is_path and not rule_obj.source_is_path:
|
if not self._is_equal_aare(self.source, self.all_source, rule_obj.source, rule_obj.all_source, 'source'):
|
||||||
if self.source != rule_obj.source:
|
|
||||||
return False
|
|
||||||
elif self.source_is_path != rule_obj.source_is_path:
|
|
||||||
return False
|
|
||||||
elif not self._is_equal_aare(self.source, self.all_source, rule_obj.source, rule_obj.all_source, 'source'):
|
|
||||||
return False
|
return False
|
||||||
if not self._is_equal_aare(self.dest, self.all_dest, rule_obj.dest, rule_obj.all_dest, 'dest'):
|
if not self._is_equal_aare(self.dest, self.all_dest, rule_obj.dest, rule_obj.all_dest, 'dest'):
|
||||||
return False
|
return False
|
||||||
@@ -256,8 +251,8 @@ class MountRule(BaseRule):
|
|||||||
elif not self.all_source and type(self.source) is not str:
|
elif not self.all_source and type(self.source) is not str:
|
||||||
self.source = self.source.glob_path()
|
self.source = self.source.glob_path()
|
||||||
if self.source.is_equal('/**/'):
|
if self.source.is_equal('/**/'):
|
||||||
self.all_source= True
|
self.all_source = True
|
||||||
self.source=self.ALL
|
self.source = self.ALL
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.options = self.ALL
|
self.options = self.ALL
|
||||||
@@ -273,15 +268,22 @@ class MountRule(BaseRule):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
_('Operation'), operation,
|
_('Operation'), operation,
|
||||||
_('Fstype'), (self.is_fstype_equal, fstype) if fstype != 'ALL' else fstype ,
|
_('Fstype'), (self.is_fstype_equal, fstype) if fstype != 'ALL' else fstype,
|
||||||
_('Options'), (self.is_options_equal, options) if options != 'ALL' else options ,
|
_('Options'), (self.is_options_equal, options) if options != 'ALL' else options,
|
||||||
_('Source'), source,
|
_('Source'), source,
|
||||||
_('Destination'), dest,
|
_('Destination'), dest,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MountRuleset(BaseRuleset):
|
class MountRuleset(BaseRuleset):
|
||||||
'''Class to handle and store a collection of Mount rules'''
|
'''Class to handle and store a collection of Mount rules'''
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_in_with_spaces(value):
|
||||||
|
''' wrap 'in' keyword in spaces, and leave everything else unchanged '''
|
||||||
|
|
||||||
|
if value == 'in':
|
||||||
|
value = ' in '
|
||||||
|
|
||||||
|
return value
|
||||||
|
@@ -28,36 +28,34 @@ _ = init_translation()
|
|||||||
class MountTestParse(AATest):
|
class MountTestParse(AATest):
|
||||||
|
|
||||||
tests = (
|
tests = (
|
||||||
# Rule Operation Filesystem Options Source Destination Audit Deny Allow Comment
|
# Rule Operation Filesystem Options Source Destination Audit Deny Allow Comment
|
||||||
('mount fstype=bpf options=rw bpf -> /sys/fs/bpf/,', MountRule('mount', ('=',('bpf')), ('=',('rw')), "bpf", "/sys/fs/bpf/", False, False, False, '' )),
|
('mount fstype=bpf options=rw bpf -> /sys/fs/bpf/,', MountRule('mount', ('=', ('bpf')), ('=', ('rw')), 'bpf', '/sys/fs/bpf/', False, False, False, '' )),
|
||||||
('mount fstype=bpf options=(rw) random_label -> /sys/fs/bpf/,', MountRule('mount', ('=',('bpf')), ('=',('rw')), "random_label", "/sys/fs/bpf/", False, False, False, '' )),
|
('mount fstype=bpf options=(rw) random_label -> /sys/fs/bpf/,', MountRule('mount', ('=', ('bpf')), ('=', ('rw')), 'random_label', '/sys/fs/bpf/', False, False, False, '' )),
|
||||||
('mount,', MountRule('mount', MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
('mount,', MountRule('mount', MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
('mount fstype=(ext3, ext4),', MountRule('mount', ('=',('ext3', 'ext4')), MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
('mount fstype=(ext3, ext4),', MountRule('mount', ('=', ('ext3', 'ext4')), MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
('mount bpf,', MountRule('mount', MountRule.ALL, MountRule.ALL, "bpf", MountRule.ALL, False, False, False, '' )),
|
('mount bpf,', MountRule('mount', MountRule.ALL, MountRule.ALL, 'bpf', MountRule.ALL, False, False, False, '' )),
|
||||||
('mount none,', MountRule('mount', MountRule.ALL, MountRule.ALL, "none", MountRule.ALL, False, False, False, '' )),
|
('mount none,', MountRule('mount', MountRule.ALL, MountRule.ALL, 'none', MountRule.ALL, False, False, False, '' )),
|
||||||
('mount fstype=(ext3, ext4) options=(ro),', MountRule('mount', ('=',('ext3', 'ext4')), ('=',('ro')), MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
('mount fstype=(ext3, ext4) options=(ro),', MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
('mount @{mntpnt},', MountRule('mount', MountRule.ALL, MountRule.ALL, "@{mntpnt}", MountRule.ALL, False, False, False, '' )),
|
('mount @{mntpnt},', MountRule('mount', MountRule.ALL, MountRule.ALL, '@{mntpnt}', MountRule.ALL, False, False, False, '' )),
|
||||||
('mount /a,', MountRule('mount', MountRule.ALL, MountRule.ALL, "/a", MountRule.ALL, False, False, False, '' )),
|
('mount /a,', MountRule('mount', MountRule.ALL, MountRule.ALL, '/a', MountRule.ALL, False, False, False, '' )),
|
||||||
('mount fstype=(ext3, ext4) /a -> /b,', MountRule('mount', ('=',('ext3', 'ext4')), MountRule.ALL, "/a", "/b", False, False, False, '' )),
|
('mount fstype=(ext3, ext4) /a -> /b,', MountRule('mount', ('=', ('ext3', 'ext4')), MountRule.ALL, '/a', '/b', False, False, False, '' )),
|
||||||
('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b,', MountRule('mount', ('=',('ext3', 'ext4')), ('=',('ro', 'rbind')), "/a", "/b", False, False, False, '' )),
|
('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b,', MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro', 'rbind')), '/a', '/b', False, False, False, '' )),
|
||||||
('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('=',('ext3', 'ext4')), ('=',('ro', 'rbind')), "/a", "/b", False, False, False, ' #cmt')),
|
('mount fstype=(ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro', 'rbind')), '/a', '/b', False, False, False, ' #cmt')),
|
||||||
('mount fstype=(ext3, ext4) options in (ro, rbind) /a -> /b,', MountRule('mount', ('=',('ext3', 'ext4')), ('in',('ro', 'rbind')), "/a", "/b", False, False, False, '' )),
|
('mount fstype=(ext3, ext4) options in (ro, rbind) /a -> /b,', MountRule('mount', ('=', ('ext3', 'ext4')), ('in', ('ro', 'rbind')), '/a', '/b', False, False, False, '' )),
|
||||||
('mount fstype in (ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('in',('ext3', 'ext4')),('=',('ro', 'rbind')), "/a", "/b", False, False, False, ' #cmt')),
|
('mount fstype in (ext3, ext4) options=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('in', ('ext3', 'ext4')), ('=', ('ro', 'rbind')), '/a', '/b', False, False, False, ' #cmt')),
|
||||||
('mount fstype in (ext3, ext4) option in (ro, rbind) /a, #cmt', MountRule('mount', ('in',('ext3', 'ext4')),('in',('ro', 'rbind')), "/a", MountRule.ALL, False, False, False, ' #cmt')),
|
('mount fstype in (ext3, ext4) option in (ro, rbind) /a, #cmt', MountRule('mount', ('in', ('ext3', 'ext4')), ('in', ('ro', 'rbind')), '/a', MountRule.ALL, False, False, False, ' #cmt')),
|
||||||
('mount fstype=(ext3, ext4) option=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('=',('ext3', 'ext4')), ('=', ('ro', 'rbind')), "/a", "/b", False, False, False, ' #cmt')),
|
('mount fstype=(ext3, ext4) option=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro', 'rbind')), '/a', '/b', False, False, False, ' #cmt')),
|
||||||
('mount options=(rw, rbind) /usr/lib{,32,64,x32}/modules/ -> /tmp/snap.rootfs_*{,/usr}/lib/modules/,',
|
('mount options=(rw, rbind) /usr/lib{,32,64,x32}/modules/ -> /tmp/snap.rootfs_*{,/usr}/lib/modules/,',
|
||||||
MountRule('mount', MountRule.ALL, ('=',('rw', 'rbind')), "/usr/lib{,32,64,x32}/modules/",
|
MountRule('mount', MountRule.ALL, ('=', ('rw', 'rbind')), '/usr/lib{,32,64,x32}/modules/',
|
||||||
"/tmp/snap.rootfs_*{,/usr}/lib/modules/",
|
'/tmp/snap.rootfs_*{,/usr}/lib/modules/',
|
||||||
False, False, False, '' )),
|
False, False, False, '' )),
|
||||||
('umount,', MountRule('umount', MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
('umount,', MountRule('umount', MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
('umount fstype=ext3,', MountRule('umount', ('=',('ext3')), MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
('umount fstype=ext3,', MountRule('umount', ('=', ('ext3')), MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
('umount /a,', MountRule('umount', MountRule.ALL, MountRule.ALL, MountRule.ALL, "/a", False, False, False, '' )),
|
('umount /a,', MountRule('umount', MountRule.ALL, MountRule.ALL, MountRule.ALL, '/a', False, False, False, '' )),
|
||||||
|
|
||||||
('remount,', MountRule('remount',MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
|
||||||
('remount fstype=ext4,', MountRule('remount',('=',('ext4')), MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
|
||||||
('remount /b,', MountRule('remount',MountRule.ALL, MountRule.ALL, MountRule.ALL, "/b", False, False, False, '' )),
|
|
||||||
|
|
||||||
|
|
||||||
|
('remount,', MountRule('remount', MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
|
('remount fstype=ext4,', MountRule('remount', ('=', ('ext4')), MountRule.ALL, MountRule.ALL, MountRule.ALL, False, False, False, '' )),
|
||||||
|
('remount /b,', MountRule('remount', MountRule.ALL, MountRule.ALL, MountRule.ALL, '/b', False, False, False, '' )),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, rawrule, expected):
|
def _run_test(self, rawrule, expected):
|
||||||
@@ -69,12 +67,12 @@ class MountTestParse(AATest):
|
|||||||
|
|
||||||
class MountTestParseInvalid(AATest):
|
class MountTestParseInvalid(AATest):
|
||||||
tests = (
|
tests = (
|
||||||
('mount fstype=,', AppArmorException),
|
('mount fstype=,', AppArmorException),
|
||||||
('mount fstype=(foo),', AppArmorException),
|
('mount fstype=(foo),', AppArmorException),
|
||||||
('mount fstype=(),', AppArmorException),
|
('mount fstype=(),', AppArmorException),
|
||||||
('mount options=(),', AppArmorException),
|
('mount options=(),', AppArmorException),
|
||||||
('mount option=(invalid),', AppArmorException),
|
('mount option=(invalid),', AppArmorException),
|
||||||
('mount option=(ext3ext4),',AppArmorException),
|
('mount option=(ext3ext4),', AppArmorException),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, rawrule, expected):
|
def _run_test(self, rawrule, expected):
|
||||||
@@ -88,60 +86,69 @@ class MountTestParseInvalid(AATest):
|
|||||||
|
|
||||||
def test_diff_non_mountrule(self):
|
def test_diff_non_mountrule(self):
|
||||||
exp = namedtuple('exp', ('audit', 'deny'))
|
exp = namedtuple('exp', ('audit', 'deny'))
|
||||||
obj = MountRule('mount',("=", '(ext4)'), MountRule.ALL, MountRule.ALL, MountRule.ALL)
|
obj = MountRule('mount', ('=', 'ext4'), MountRule.ALL, MountRule.ALL, MountRule.ALL)
|
||||||
with self.assertRaises(AppArmorBug):
|
with self.assertRaises(AppArmorBug):
|
||||||
obj.is_equal(exp(False, False), False)
|
obj.is_equal(exp(False, False), False)
|
||||||
|
|
||||||
def test_diff_invalid_fstype_equals_or_in(self):
|
def test_diff_invalid_fstype_equals_or_in(self):
|
||||||
with self.assertRaises(AppArmorBug):
|
with self.assertRaises(AppArmorBug):
|
||||||
MountRule('mount', ('ext3', '(ext4)'), MountRule.ALL, MountRule.ALL, MountRule.ALL) # fstype[0] should be '=' or 'in'
|
MountRule('mount', ('ext3', 'ext4'), MountRule.ALL, MountRule.ALL, MountRule.ALL) # fstype[0] should be '=' or 'in'
|
||||||
|
|
||||||
|
def test_diff_invalid_fstype_keyword(self):
|
||||||
|
with self.assertRaises(AppArmorException):
|
||||||
|
MountRule('mount', ('=', 'invalidfs'), MountRule.ALL, MountRule.ALL, MountRule.ALL) # fstype[0] should be '=' or 'in'
|
||||||
|
|
||||||
def test_diff_invalid_options_equals_or_in(self):
|
def test_diff_invalid_options_equals_or_in(self):
|
||||||
with self.assertRaises(AppArmorBug):
|
with self.assertRaises(AppArmorBug):
|
||||||
MountRule('mount', MountRule.ALL, ('rbind', '(rw)'), MountRule.ALL, MountRule.ALL) # fstype[0] should be '=' or 'in'
|
MountRule('mount', MountRule.ALL, ('rbind', 'rw'), MountRule.ALL, MountRule.ALL) # fstype[0] should be '=' or 'in'
|
||||||
|
|
||||||
|
def test_diff_invalid_options_keyword(self):
|
||||||
|
with self.assertRaises(AppArmorException):
|
||||||
|
MountRule('mount', MountRule.ALL, ('=', 'invalid'), MountRule.ALL, MountRule.ALL) # fstype[0] should be '=' or 'in'
|
||||||
|
|
||||||
def test_diff_fstype(self):
|
def test_diff_fstype(self):
|
||||||
obj1 = MountRule('mount',("=", '(ext4)'), MountRule.ALL, MountRule.ALL, MountRule.ALL)
|
obj1 = MountRule('mount', ('=', 'ext4'), MountRule.ALL, MountRule.ALL, MountRule.ALL)
|
||||||
obj2 = MountRule('mount',MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL)
|
obj2 = MountRule('mount', MountRule.ALL, MountRule.ALL, MountRule.ALL, MountRule.ALL)
|
||||||
self.assertFalse(obj1.is_equal(obj2, False))
|
self.assertFalse(obj1.is_equal(obj2, False))
|
||||||
|
|
||||||
def test_diff_source(self):
|
def test_diff_source(self):
|
||||||
obj1 = MountRule('mount',MountRule.ALL, MountRule.ALL, "/foo", MountRule.ALL)
|
obj1 = MountRule('mount', MountRule.ALL, MountRule.ALL, '/foo', MountRule.ALL)
|
||||||
obj2 = MountRule('mount',MountRule.ALL, MountRule.ALL, "/bar", MountRule.ALL)
|
obj2 = MountRule('mount', MountRule.ALL, MountRule.ALL, '/bar', MountRule.ALL)
|
||||||
self.assertFalse(obj1.is_equal(obj2, False))
|
self.assertFalse(obj1.is_equal(obj2, False))
|
||||||
|
|
||||||
def test_invalid_umount_with_source(self):
|
def test_invalid_umount_with_source(self):
|
||||||
with self.assertRaises(AppArmorException):
|
with self.assertRaises(AppArmorException):
|
||||||
MountRule('umount', MountRule.ALL, MountRule.ALL, "/foo", MountRule.ALL) # Umount and remount shall not have a source
|
MountRule('umount', MountRule.ALL, MountRule.ALL, '/foo', MountRule.ALL) # Umount and remount shall not have a source
|
||||||
|
|
||||||
def test_invalid_remount_with_source(self):
|
def test_invalid_remount_with_source(self):
|
||||||
with self.assertRaises(AppArmorException):
|
with self.assertRaises(AppArmorException):
|
||||||
MountRule('remount', MountRule.ALL, MountRule.ALL, "/foo", MountRule.ALL)
|
MountRule('remount', MountRule.ALL, MountRule.ALL, '/foo', MountRule.ALL)
|
||||||
|
|
||||||
|
|
||||||
class MountTestFilesystems(AATest):
|
class MountTestFilesystems(AATest):
|
||||||
def test_fs(self):
|
def test_fs(self):
|
||||||
with open("/proc/filesystems") as f:
|
with open('/proc/filesystems') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
self.assertTrue(line.split()[-1] in valid_fs)
|
fs_name = line.split()[-1]
|
||||||
|
self.assertTrue(fs_name in valid_fs, '/proc/filesystems contains %s which is not listed in MountRule valid_fs' % fs_name)
|
||||||
|
|
||||||
|
|
||||||
class MountTestGlob(AATest):
|
class MountTestGlob(AATest):
|
||||||
def test_glob(self):
|
def test_glob(self):
|
||||||
globList = [(
|
globList = [(
|
||||||
"mount options=(bind, rw) /home/user/Downloads/ -> /mnt/a/,",
|
'mount options=(bind, rw) /home/user/Downloads/ -> /mnt/a/,',
|
||||||
"mount options=(bind, rw) /home/user/Downloads/,",
|
'mount options=(bind, rw) /home/user/Downloads/,',
|
||||||
"mount options=(bind, rw) /home/user/*/,",
|
'mount options=(bind, rw) /home/user/*/,',
|
||||||
"mount options=(bind, rw) /home/**/,",
|
'mount options=(bind, rw) /home/**/,',
|
||||||
"mount options=(bind, rw),",
|
'mount options=(bind, rw),',
|
||||||
"mount,",
|
'mount,',
|
||||||
"mount,",
|
'mount,',
|
||||||
)]
|
)]
|
||||||
for globs in globList:
|
for globs in globList:
|
||||||
for i in range(len(globs)-1):
|
for i in range(len(globs) - 1):
|
||||||
rule = MountRule.create_instance(globs[i])
|
rule = MountRule.create_instance(globs[i])
|
||||||
rule.glob()
|
rule.glob()
|
||||||
self.assertEqual(rule.get_clean(), globs[i+1])
|
self.assertEqual(rule.get_clean(), globs[i + 1])
|
||||||
|
|
||||||
|
|
||||||
class MountTestClean(AATest):
|
class MountTestClean(AATest):
|
||||||
@@ -152,7 +159,13 @@ class MountTestClean(AATest):
|
|||||||
(' mount fstype = ( sysfs , procfs ) , ', 'mount fstype=(procfs, sysfs),'),
|
(' mount fstype = ( sysfs , procfs ) , ', 'mount fstype=(procfs, sysfs),'),
|
||||||
(' mount options = ( rw ) , ', 'mount options=(rw),'),
|
(' mount options = ( rw ) , ', 'mount options=(rw),'),
|
||||||
(' mount options = ( rw , noatime ) , ', 'mount options=(noatime, rw),'),
|
(' mount options = ( rw , noatime ) , ', 'mount options=(noatime, rw),'),
|
||||||
|
(' mount fstype in ( sysfs ) , ', 'mount fstype in (sysfs),'),
|
||||||
|
(' mount fstype in ( sysfs , procfs ) , ', 'mount fstype in (procfs, sysfs),'),
|
||||||
|
(' mount options in ( rw ) , ', 'mount options in (rw),'),
|
||||||
|
(' mount options in ( rw , noatime ) , ', 'mount options in (noatime, rw),'),
|
||||||
|
(' umount , ', 'umount,'),
|
||||||
(' umount /foo , ', 'umount /foo,'),
|
(' umount /foo , ', 'umount /foo,'),
|
||||||
|
(' remount , ', 'remount,'),
|
||||||
(' remount /foo , ', 'remount /foo,'),
|
(' remount /foo , ', 'remount /foo,'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -165,11 +178,12 @@ class MountTestClean(AATest):
|
|||||||
self.assertEqual(expected, clean, 'unexpected clean rule')
|
self.assertEqual(expected, clean, 'unexpected clean rule')
|
||||||
self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule')
|
self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule')
|
||||||
|
|
||||||
|
|
||||||
class MountLogprofHeaderTest(AATest):
|
class MountLogprofHeaderTest(AATest):
|
||||||
tests = (
|
tests = (
|
||||||
('mount,', [_('Operation'), _('mount'), _('Fstype'), _('ALL'), _('Options'), _('ALL'), _('Source'), _('ALL'), _('Destination'), _('ALL')]),
|
('mount,', [_('Operation'), _('mount'), _('Fstype'), _('ALL'), _('Options'), _('ALL'), _('Source'), _('ALL'), _('Destination'), _('ALL')]),
|
||||||
('mount options=(ro, nosuid) /a,', [_('Operation'), _('mount'), _('Fstype'), _('ALL'), _('Options'), ("=", _('nosuid ro')),_('Source'), _('/a'), _('Destination'), _('ALL')]),
|
('mount options=(ro, nosuid) /a,', [_('Operation'), _('mount'), _('Fstype'), _('ALL'), _('Options'), ('=', _('nosuid ro')), _('Source'), _('/a'), _('Destination'), _('ALL')]),
|
||||||
('mount fstype=(ext3, ext4) options=(ro, nosuid) /a -> /b,', [_('Operation'), _('mount'), _('Fstype'), ("=", _('ext3 ext4')),_('Options'), ("=", _('nosuid ro')),_('Source'), _('/a'), _('Destination'), _('/b')])
|
('mount fstype=(ext3, ext4) options=(ro, nosuid) /a -> /b,', [_('Operation'), _('mount'), _('Fstype'), ('=', _('ext3 ext4')), _('Options'), ('=', _('nosuid ro')), _('Source'), _('/a'), _('Destination'), _('/b')])
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, params, expected):
|
def _run_test(self, params, expected):
|
||||||
@@ -179,47 +193,45 @@ class MountLogprofHeaderTest(AATest):
|
|||||||
|
|
||||||
class MountIsCoveredTest(AATest):
|
class MountIsCoveredTest(AATest):
|
||||||
def test_is_covered(self):
|
def test_is_covered(self):
|
||||||
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b*", "/b*")
|
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b*', '/b*')
|
||||||
tests = [
|
tests = [
|
||||||
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b", "/bar"),
|
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b', '/bar'),
|
||||||
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/bar", "/b")
|
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/bar', '/b')
|
||||||
]
|
]
|
||||||
for test in tests:
|
for test in tests:
|
||||||
self.assertTrue(obj.is_covered(MountRule(*test)))
|
self.assertTrue(obj.is_covered(MountRule(*test)))
|
||||||
self.assertFalse(obj.is_equal(MountRule(*test)))
|
self.assertFalse(obj.is_equal(MountRule(*test)))
|
||||||
|
|
||||||
def test_is_covered_fs_source(self):
|
def test_is_covered_fs_source(self):
|
||||||
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "tmpfs", MountRule.ALL)
|
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'tmpfs', MountRule.ALL)
|
||||||
self.assertTrue(obj.is_covered(MountRule("mount", ("=", ('ext3')), ("=", ('ro')), "tmpfs", MountRule.ALL)))
|
self.assertTrue(obj.is_covered(MountRule('mount', ('=', ('ext3')), ('=', ('ro')), 'tmpfs', MountRule.ALL)))
|
||||||
self.assertFalse(obj.is_equal(MountRule("mount", ("=", ('ext3')), ("=", ('ro')), "tmpfs", MountRule.ALL)))
|
self.assertFalse(obj.is_equal(MountRule('mount', ('=', ('ext3')), ('=', ('ro')), 'tmpfs', MountRule.ALL)))
|
||||||
|
|
||||||
def test_is_notcovered(self):
|
def test_is_notcovered(self):
|
||||||
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b*", "/b*")
|
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b*', '/b*')
|
||||||
tests = [
|
tests = [
|
||||||
("mount", ("in", ('ext3', 'ext4')), ("=", ('ro')), "/foo/bar", "/bar" ),
|
('mount', ('in', ('ext3', 'ext4')), ('=', ('ro')), '/foo/bar', '/bar' ),
|
||||||
("mount", ("=", ('procfs, ext4')), ("=", ('ro')), "/foo/bar", "/bar" ),
|
('mount', ('=', ('procfs', 'ext4')), ('=', ('ro')), '/foo/bar', '/bar' ),
|
||||||
("mount", ("=", ('ext3')), ("=", ('rw')), "/foo/bar", "/bar" ),
|
('mount', ('=', ('ext3')), ('=', ('rw')), '/foo/bar', '/bar' ),
|
||||||
("mount", ("=", ('ext3', 'ext4')), MountRule.ALL, "/foo/b*", "/bar" ),
|
('mount', ('=', ('ext3', 'ext4')), MountRule.ALL, '/foo/b*', '/bar' ),
|
||||||
("mount", MountRule.ALL, ("=", ('ro')), "/foo/b*", "/bar" ),
|
('mount', MountRule.ALL, ('=', ('ro')), '/foo/b*', '/bar' ),
|
||||||
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/invalid/bar", "/bar" ),
|
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/invalid/bar', '/bar' ),
|
||||||
("umount", MountRule.ALL, MountRule.ALL, MountRule.ALL, "/bar" ),
|
('umount', MountRule.ALL, MountRule.ALL, MountRule.ALL, '/bar' ),
|
||||||
("remount", MountRule.ALL, MountRule.ALL, MountRule.ALL, "/bar" ),
|
('remount', MountRule.ALL, MountRule.ALL, MountRule.ALL, '/bar' ),
|
||||||
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "tmpfs", "/bar" ),
|
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'tmpfs', '/bar' ),
|
||||||
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b*", "/invalid" ),
|
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b*', '/invalid'),
|
||||||
]
|
]
|
||||||
for test in tests:
|
for test in tests:
|
||||||
self.assertFalse(obj.is_covered(MountRule(*test)))
|
self.assertFalse(obj.is_covered(MountRule(*test)))
|
||||||
self.assertFalse(obj.is_equal(MountRule(*test)))
|
self.assertFalse(obj.is_equal(MountRule(*test)))
|
||||||
|
|
||||||
|
|
||||||
def test_is_not_covered_fs_source(self):
|
def test_is_not_covered_fs_source(self):
|
||||||
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "tmpfs", MountRule.ALL)
|
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'tmpfs', MountRule.ALL)
|
||||||
test = ("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "procfs", MountRule.ALL)
|
test = ('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'procfs', MountRule.ALL)
|
||||||
self.assertFalse(obj.is_covered(MountRule(*test)))
|
self.assertFalse(obj.is_covered(MountRule(*test)))
|
||||||
self.assertFalse(obj.is_equal(MountRule(*test)))
|
self.assertFalse(obj.is_equal(MountRule(*test)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setup_all_loops(__name__)
|
setup_all_loops(__name__)
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=1)
|
unittest.main(verbosity=1)
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
#! /usr/bin/python3
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# 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 unittest
|
|
||||||
|
|
||||||
import apparmor.aa as aa
|
|
||||||
from common_test import AAParseTest, setup_aa, setup_regex_tests
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAAParseMountTest(AAParseTest):
|
|
||||||
def setUp(self):
|
|
||||||
self.parse_function = aa.parse_mount_rule
|
|
||||||
|
|
||||||
|
|
||||||
class AAParseMountTest(BaseAAParseMountTest):
|
|
||||||
tests = (
|
|
||||||
('mount,', 'mount base keyword rule'),
|
|
||||||
('mount -o ro,', 'mount ro rule'),
|
|
||||||
('mount -o rw /dev/sdb1 -> /mnt/external,', 'mount rw with mount point'),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AAParseRemountTest(BaseAAParseMountTest):
|
|
||||||
tests = (
|
|
||||||
('remount,', 'remount base keyword rule'),
|
|
||||||
('remount -o ro,', 'remount ro rule'),
|
|
||||||
('remount -o ro /,', 'remount ro with mountpoint'),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AAParseUmountTest(BaseAAParseMountTest):
|
|
||||||
tests = (
|
|
||||||
('umount,', 'umount base keyword rule'),
|
|
||||||
('umount /mnt/external,', 'umount with mount point'),
|
|
||||||
('unmount,', 'unmount base keyword rule'),
|
|
||||||
('unmount /mnt/external,', 'unmount with mount point'),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
setup_aa(aa)
|
|
||||||
if __name__ == '__main__':
|
|
||||||
setup_regex_tests(AAParseMountTest)
|
|
||||||
setup_regex_tests(AAParseRemountTest)
|
|
||||||
setup_regex_tests(AAParseUmountTest)
|
|
||||||
unittest.main(verbosity=1)
|
|
@@ -85,6 +85,16 @@ exception_not_raised = (
|
|||||||
'mount/bad_1.sd',
|
'mount/bad_1.sd',
|
||||||
'mount/bad_2.sd',
|
'mount/bad_2.sd',
|
||||||
|
|
||||||
|
# not checked/detected: "make-*" mount opt and an invalid src
|
||||||
|
'mount/bad_opt_17.sd',
|
||||||
|
'mount/bad_opt_18.sd',
|
||||||
|
'mount/bad_opt_19.sd',
|
||||||
|
'mount/bad_opt_20.sd',
|
||||||
|
'mount/bad_opt_21.sd',
|
||||||
|
'mount/bad_opt_22.sd',
|
||||||
|
'mount/bad_opt_23.sd',
|
||||||
|
'mount/bad_opt_24.sd',
|
||||||
|
|
||||||
'profile/flags/flags_bad10.sd',
|
'profile/flags/flags_bad10.sd',
|
||||||
'profile/flags/flags_bad11.sd',
|
'profile/flags/flags_bad11.sd',
|
||||||
'profile/flags/flags_bad12.sd',
|
'profile/flags/flags_bad12.sd',
|
||||||
@@ -314,15 +324,8 @@ unknown_line = (
|
|||||||
'bare_include_tests/ok_85.sd',
|
'bare_include_tests/ok_85.sd',
|
||||||
'bare_include_tests/ok_86.sd',
|
'bare_include_tests/ok_86.sd',
|
||||||
|
|
||||||
# option = make-${valid-option} (e.g. make-private) is not supported
|
# mount with fstype using AARE
|
||||||
'mount/ok_opt_48.sd',
|
'mount/ok_12.sd',
|
||||||
'mount/ok_opt_49.sd',
|
|
||||||
'mount/ok_opt_50.sd',
|
|
||||||
'mount/ok_opt_51.sd',
|
|
||||||
'mount/ok_opt_52.sd',
|
|
||||||
'mount/ok_opt_53.sd',
|
|
||||||
'mount/ok_opt_54.sd',
|
|
||||||
'mount/ok_opt_55.sd',
|
|
||||||
|
|
||||||
# Mount with flags in {remount, [r]unbindable, [r]shared, [r]private, [r]slave} does not support a source
|
# Mount with flags in {remount, [r]unbindable, [r]shared, [r]private, [r]slave} does not support a source
|
||||||
'mount/ok_opt_68.sd',
|
'mount/ok_opt_68.sd',
|
||||||
@@ -334,15 +337,7 @@ unknown_line = (
|
|||||||
'mount/ok_opt_74.sd',
|
'mount/ok_opt_74.sd',
|
||||||
'mount/ok_opt_75.sd',
|
'mount/ok_opt_75.sd',
|
||||||
|
|
||||||
# option = make-${valid-option} (e.g. make-private) is not supported
|
# options=slave with /** src (first rule in the test causes exception)
|
||||||
'mount/ok_opt_76.sd',
|
|
||||||
'mount/ok_opt_77.sd',
|
|
||||||
'mount/ok_opt_78.sd',
|
|
||||||
'mount/ok_opt_79.sd',
|
|
||||||
'mount/ok_opt_80.sd',
|
|
||||||
'mount/ok_opt_81.sd',
|
|
||||||
'mount/ok_opt_82.sd',
|
|
||||||
'mount/ok_opt_83.sd',
|
|
||||||
'mount/ok_opt_84.sd',
|
'mount/ok_opt_84.sd',
|
||||||
|
|
||||||
# According to spec mount should be in the form fstype=... options=... and NOT in the form options=... fstype=...
|
# According to spec mount should be in the form fstype=... options=... and NOT in the form options=... fstype=...
|
||||||
@@ -351,8 +346,6 @@ unknown_line = (
|
|||||||
'mount/ok_opt_combo_1.sd',
|
'mount/ok_opt_combo_1.sd',
|
||||||
'mount/ok_opt_combo_4.sd',
|
'mount/ok_opt_combo_4.sd',
|
||||||
|
|
||||||
# Invalid keyword: read-only --> Should be ro
|
|
||||||
'mount/ok_opt_3.sd',
|
|
||||||
# Options should be comma separated
|
# Options should be comma separated
|
||||||
'mount/in_4.sd', # also order option then fstype is invalid
|
'mount/in_4.sd', # also order option then fstype is invalid
|
||||||
)
|
)
|
||||||
|
@@ -14,7 +14,7 @@ import unittest
|
|||||||
import apparmor.aa as aa
|
import apparmor.aa as aa
|
||||||
from apparmor.common import AppArmorBug, AppArmorException
|
from apparmor.common import AppArmorBug, AppArmorException
|
||||||
from apparmor.regex import (
|
from apparmor.regex import (
|
||||||
RE_PROFILE_CAP, RE_PROFILE_DBUS, RE_PROFILE_PTRACE, RE_PROFILE_SIGNAL,
|
RE_PROFILE_CAP, RE_PROFILE_DBUS, RE_PROFILE_MOUNT, RE_PROFILE_PTRACE, RE_PROFILE_SIGNAL,
|
||||||
RE_PROFILE_START, parse_profile_start_line, re_match_include,
|
RE_PROFILE_START, parse_profile_start_line, re_match_include,
|
||||||
re_match_include_parse, strip_parenthesis, strip_quotes)
|
re_match_include_parse, strip_parenthesis, strip_quotes)
|
||||||
from common_test import AATest, setup_aa, setup_all_loops
|
from common_test import AATest, setup_aa, setup_all_loops
|
||||||
@@ -248,7 +248,7 @@ class AARegexMount(AARegexTest):
|
|||||||
"""Tests for RE_PROFILE_MOUNT"""
|
"""Tests for RE_PROFILE_MOUNT"""
|
||||||
|
|
||||||
def AASetup(self):
|
def AASetup(self):
|
||||||
self.regex = aa.RE_PROFILE_MOUNT
|
self.regex = RE_PROFILE_MOUNT
|
||||||
|
|
||||||
tests = (
|
tests = (
|
||||||
(' mount,', (None, None, 'mount,', 'mount', None, None)),
|
(' mount,', (None, None, 'mount,', 'mount', None, None)),
|
||||||
|
Reference in New Issue
Block a user