2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-30 22:05:27 +00:00

Compare commits

...

30 Commits

Author SHA1 Message Date
John Johansen
353ba896d4 Prepare for AppArmor 4.0 beta2 release
- update version file

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-03-06 17:06:45 -08:00
John Johansen
c13007f7fc Merge libapparmor: check if AX_CHECK_COMPILE_FLAG is available
The error message when autoconf-archive is not installed is not very
intuitive:

```
./configure: line 14422: EXTRA_WARNINGS: command not found
./configure: line 14423: syntax error near unexpected token `-flto-partition=none,'
./configure: line 14423: `AX_CHECK_COMPILE_FLAG(-flto-partition=none, , , -Werror)'
```

So, check if AX_CHECK_COMPILE_FLAG is defined and if not, complain
that autoconf-archive is missing.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1174
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2024-03-06 21:21:54 +00:00
John Johansen
88a420853e Merge parser: fix policy generation for non-af_inet rules
The layout for AF_INET and AF_INET6 rules were being applied to all
families, which causes failures in their mediation.

Fixes: ddefe11a ("parser: add fine grained conditionals to network rule")
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1175
Merged-by: John Johansen <john@jjmx.net>
2024-03-06 21:19:21 +00:00
Georgia Garcia
2db41acd1b parser: fix generic perms in network rules
The permission for network rules when the inet mediation was not
available, or for when the family was not af_inet or af_inet6 was
being generated as one that would allow anything. Make them specific
using perms.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-06 10:33:54 -03:00
Georgia Garcia
a10d9044b8 parser: fix policy generation for non-af_inet rules
The layout for AF_INET and AF_INET6 rules were being applied to all
families, which causes failures in their mediation.

Fixes: ddefe11a ("parser: add fine grained conditionals to network rule")
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-06 10:07:17 -03:00
Christian Boltz
b53441a689 Merge Update ancient paths in apparmor and apparmor.d manpage
- replace example calls of /etc/init.d/apparmor with apparmor.service
- drop /etc/init.d/apparmor in filelist
- replace /var/lib/apparmor/ with /var/cache/apparmor/

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1171
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2024-03-06 12:24:13 +00:00
Christian Boltz
7452f34279 Update ancient paths in apparmor and apparmor.d manpage
- replace example calls of /etc/init.d/apparmor with apparmor.service
- drop /etc/init.d/apparmor in filelist
- replace /var/lib/apparmor/ with /var/cache/apparmor/
2024-03-05 22:59:18 +01:00
Georgia Garcia
aedb8a5b00 libapparmor: check if AX_CHECK_COMPILE_FLAG is available
The error message when autoconf-archive is not installed is not very
intuitive:

./configure: line 14422: EXTRA_WARNINGS: command not found
./configure: line 14423: syntax error near unexpected token `-flto-partition=none,'
./configure: line 14423: `AX_CHECK_COMPILE_FLAG(-flto-partition=none, , , -Werror)'

So, check if AX_CHECK_COMPILE_FLAG is defined and if not, complain
that autoconf-archive is missing.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-04 10:27:34 -03:00
Christian Boltz
6695944c2c Merge utils: fix coding style in mount
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1173
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2024-03-04 13:05:41 +00:00
Georgia Garcia
01090dcf1b utils: fix coding style in mount
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-04 09:24:58 -03:00
Georgia Garcia
3ea2bfec56 Merge Small fixes in MountRule
- Removed unnecessary variable source_is_path in mount rules
- Changed a string to a r-string to avoid an 'invalid escape sequence \s' warning

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1172
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-04 11:53:51 +00:00
Georgia Garcia
3d1a867c0a Merge Update mailinglist and homepage in changehat READMEs
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1170
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-04 11:53:10 +00:00
Georgia Garcia
dfb02cbd93 Merge MountRule: check for unknown fstype and options keywords, and fix issues uncovered by that
* **MountRule: sync flags_keywords with parser code**

    ... based on /mount.cc mnt_opts_table

    Several keywords and aliases were missing in flags_keywords:
    - B
    - M
    - make-private
    - make-rprivate
    - make-rshared
    - make-rslave
    - make-runbindable
    - make-shared
    - make-slave
    - make-unbindable
    - r
    - R
    - read-only
    - w

    Also sort the keywords in the same order as in mount.cc.

    Note: AARE handling is still a TODO.

    After that, update the list of known parsing failures:
    - several valid profiles are now correctly parsed
    - some `"make-*" mount opt and an invalid src` bad profiles are no
      longer detected as being invalid

* **test-mount.py: fix MountRule instance creation**

    If fstype or options is a str, it has to be exactly one keyword, because
    \__init__() / check_and_split_list() won't parse a str.

    Our "normal" code already honors this, and only hands over fstype and
    options as sets or a single-keyword str.

    However, a few tests (wrongly) handed over a str that would need further
    parsing. Adjust the tests to no longer do this.

* **MountRule: check for unknown fstype and options**

    ... now that the previous commits fixed issues that ended up as unknown
    keywords.

    Also add mount/ok_12.sd as known-failing test. It uses fstype=AARE which
    MountRule doesn't support (yet?).

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1169
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-04 11:52:53 +00:00
Georgia Garcia
90f056c1c6 Merge Several MountRule fixes and improvements
* Fix writing 'mount {options,fstype} in ...' rules

We need spaces around the 'in' keyword.

Also add some tests for this.

* Make error check more readable

* MountRule: make get_clean() more readable

... by getting rid of two mostly-identical, big return statements.

Also add tests for bare umound and remount rules to ensure full test
coverage.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1168
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-03-04 11:52:33 +00:00
Maxime Bélair
0daf3e8c9b Changing string to r-string to avoid warning 2024-03-04 09:02:24 +01:00
Maxime Bélair
a86c1bd45a Remove unnecessary variable source_is_path in mount rules 2024-03-04 09:00:58 +01:00
Christian Boltz
8f4073ecd9 MountRule: check for unknown fstype and options
... now that the previous commits fixed issues that ended up as unknown
keywords.

Also add mount/ok_12.sd as known-failing test. It uses fstype=AARE which
MountRule doesn't support (yet?).
2024-03-03 21:30:49 +01:00
Christian Boltz
440be71c12 Update mailinglist and homepage in changehat READMEs 2024-03-03 18:12:06 +01:00
Christian Boltz
8d21f01924 test-mount.py: fix MountRule instance creation
If fstype or options is a str, it has to be exactly one keyword, because
__init__() / check_and_split_list() won't parse a str.

Our "normal" code already honors this, and only hands over fstype and
options as sets or a single-keyword str.

However, a few tests (wrongly) handed over a str that would need further
parsing. Adjust the tests to no longer do this.
2024-03-03 15:52:14 +01:00
Christian Boltz
4e546291a5 MountRule: sync flags_keywords with parser code
... based on /mount.cc mnt_opts_table

Several keywords and aliases were missing in flags_keywords:
- B
- M
- make-private
- make-rprivate
- make-rshared
- make-rslave
- make-runbindable
- make-shared
- make-slave
- make-unbindable
- r
- R
- read-only
- w

Also sort the keywords in the same order as in mount.cc.

Note: AARE handling is still a TODO.

After that, update the list of known parsing failures:
- several valid profiles are now correctly parsed
- some `"make-*" mount opt and an invalid src` bad profiles are no
  longer detected as being invalid
2024-03-03 15:37:59 +01:00
Christian Boltz
8c026077d6 MountRule: make get_clean() more readable
... by getting rid of two mostly-identical, big return statements.

Also add tests for bare umound and remount rules to ensure full test
coverage.
2024-03-03 13:09:43 +01:00
Christian Boltz
5e4c4a0cb3 Make error check more readable 2024-03-03 12:53:49 +01:00
Christian Boltz
9c27a7c435 Fix writing 'mount {options,fstype} in ...' rules
We need spaces around the 'in' keyword.

Also add some tests for this.
2024-03-03 12:49:57 +01:00
Christian Boltz
a367c07437 Merge Add useful error message in test-mount.py
If /proc/filesystems contains a filesystem that is not listed in
MountRule valid_fs, print a useful error message that says what exactly
is going on, instead of only saying "False is not True".

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1166
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2024-03-01 21:30:33 +00:00
Christian Boltz
2200013088 Merge Cleanup old handling of mount rules
Now that we have MountRule and MountRuleset, drop the old "just store
the whole rule" code for mount rules.

Also drop some old tests that used that "store the whole mount rule"
code, and adjust the regex_matches tests to import the regex directly
from apparmor.regex.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1165
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2024-03-01 21:29:48 +00:00
Christian Boltz
d5afc33c40 Merge MountRule: Fix typo in 'btrfs', and add '9p' filesystem
The `9p` filesystem is available during the build in build.opensuse.org.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1164
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2024-03-01 21:27:44 +00:00
Christian Boltz
517e7c96c8 MountRule: add '9p' filesystem
This filesystem is available during the build in build.opensuse.org
2024-03-01 21:36:26 +01:00
Christian Boltz
a7cd59819e Add useful error message in test-mount.py
If /proc/filesystems contains a filesystem that is not listed in
MountRule valid_fs, print a useful error message that says what exactly
is going on, instead of only saying "False is not True".
2024-03-01 20:34:11 +01:00
Christian Boltz
e7f5ee3271 MountRule: Fix typo in 'btrfs' 2024-03-01 19:49:21 +01:00
Christian Boltz
da75b1c8d8 Cleanup old handling of mount rules
Now that we have MountRule and MountRuleset, drop the old "just store
the whole rule" code for mount rules.

Also drop some old tests that used that "store the whole mount rule"
code, and adjust the regex_matches tests to import the regex directly
from apparmor.regex.
2024-03-01 19:46:02 +01:00
15 changed files with 201 additions and 280 deletions

View File

@@ -67,10 +67,10 @@ to syslog.
References
----------
Project webpage:
http://developer.novell.com/wiki/index.php/Novell_AppArmor
https://apparmor.net/
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.
See also: change_hat(3), and the Linux-PAM online documentation at

View File

@@ -188,10 +188,9 @@ parent context.
8. Feedback/Resources
-----------------
To provide feedback or ask questions please contact the
apparmor-dev@forge.novell.com mail list. This is the development list for the
AppArmor team.
Project webpage:
https://apparmor.net/
To provide feedback or ask questions please contact the
apparmor@lists.ubuntu.com mail list. This is the development list
for the AppArmor team.

View File

@@ -188,10 +188,9 @@ parent context.
8. Feedback/Resources
-----------------
To provide feedback or ask questions please contact the
apparmor-dev@forge.novell.com mail list. This is the development list for the
AppArmor team.
Project webpage:
https://apparmor.net/
To provide feedback or ask questions please contact the
apparmor@lists.ubuntu.com mail list. This is the development list
for the AppArmor team.

View File

@@ -1 +1 @@
4.0.0~beta1
4.0.0~beta2

View File

@@ -92,6 +92,7 @@ if test "$ac_cv_prog_cc_c99" = "no"; then
AC_MSG_ERROR([C99 mode is required to build libapparmor])
fi
m4_ifndef([AX_CHECK_COMPILE_FLAG], [AC_MSG_ERROR(['autoconf-archive' missing])])
EXTRA_CFLAGS="-Wall $(EXTRA_WARNINGS) -fPIC"
AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror])
AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes],

View File

@@ -2037,8 +2037,6 @@ An example AppArmor profile:
=over 4
=item F</etc/init.d/boot.apparmor>
=item F</etc/apparmor.d/>
=back

View File

@@ -36,12 +36,11 @@ of resources. AppArmor's unique security model is to bind access control
attributes to programs rather than to users.
AppArmor confinement is provided via I<profiles> loaded into the kernel
via apparmor_parser(8), typically through the F</etc/init.d/apparmor>
SysV initscript, which is used like this:
via apparmor_parser(8), typically through the F<apparmor.service>
systemd unit, which is used like this:
# /etc/init.d/apparmor start
# /etc/init.d/apparmor stop
# /etc/init.d/apparmor restart
# systemctl start apparmor
# systemctl reload apparmor
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
=item F</etc/init.d/apparmor>
=item F</etc/apparmor.d/>
=item F</var/lib/apparmor/>
=item F</var/cache/apparmor/>
=item F</var/log/audit/audit.log>

View File

@@ -234,6 +234,7 @@ struct mnt_keyword_table {
unsigned int clear;
};
// keep in sync with utils/apparmor/rule/mount.py flags_keywords
static struct mnt_keyword_table mnt_opts_table[] = {
{"ro", MS_RDONLY, 0},
{"r", MS_RDONLY, 0},

View File

@@ -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);
}
if (!features_supports_inet) {
if (!features_supports_inet || (family != AF_INET && family != AF_INET6)) {
buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(AA_VALID_NET_PERMS),
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(AA_VALID_NET_PERMS) : 0,
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(perms) : 0,
parseopts))
return false;
return true;

View File

@@ -39,7 +39,7 @@ from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, ruletyp
from apparmor.regex import (
RE_HAS_COMMENT_SPLIT, RE_PROFILE_CHANGE_HAT, RE_PROFILE_CONDITIONAL,
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)
from apparmor.rule.abi import AbiRule
from apparmor.rule.capability import CapabilityRule
@@ -1995,29 +1995,6 @@ def parse_profile_data(data, file, do_include, in_preamble):
# Conditional Boolean defined
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):
matches = RE_PROFILE_PIVOT_ROOT.search(line).groups()
@@ -2202,11 +2179,6 @@ def split_to_merged(profile_data):
return merged
def parse_mount_rule(line):
# XXX Do real parsing here
return aarules.Raw_Mount_Rule(line)
def parse_pivot_root_rule(line):
# XXX Do real parsing here
return aarules.Raw_Pivot_Root_Rule(line)

View File

@@ -16,7 +16,7 @@ import re
from apparmor.common import AppArmorBug, AppArmorException
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.translations import init_translation
@@ -33,40 +33,44 @@ valid_fs = [
'sysfs', 'tmpfs', 'bdevfs', 'procfs', 'cgroup', 'cgroup2', 'cpuset', 'devtmpfs', 'configfs', 'debugfs', 'tracefs',
'securityfs', 'sockfs', 'bpf', 'npipefs', 'ramfs', 'hugetlbfs', 'devpts', 'ext3', 'ext2', 'ext4', 'squashfs',
'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 = [
'ro', 'rw', 'nosuid', 'suid', 'nodev', 'dev', 'noexec', 'exec', 'sync', 'async', 'remount', 'mand', 'nomand',
'dirsync', 'noatime', 'atime', 'nodiratime', 'diratime', 'bind', 'rbind', 'move', 'verbose', 'silent', 'loud',
'acl', 'noacl', 'unbindable', 'runbindable', 'private', 'rprivate', 'slave', 'rslave', 'shared', 'rshared',
'relatime', 'norelatime', 'iversion', 'noiversion', 'strictatime', 'nostrictatime', 'lazytime', 'nolazytime',
'nouser', 'user', 'symfollow', 'nosymfollow', '([A-Za-z0-9]|AARE)', # TODO: handle AARE
# keep in sync with parser/mount.cc mnt_opts_table!
'ro', 'r', 'read-only', 'rw', 'w', 'suid', 'nosuid', 'dev', 'nodev', 'exec', 'noexec', 'sync', 'async', 'remount',
'mand', 'nomand', 'dirsync', 'symfollow', 'nosymfollow', 'atime', 'noatime', 'diratime', 'nodiratime', 'bind', 'B',
'move', 'M', 'rbind', 'R', 'verbose', 'silent', 'loud', 'acl', 'noacl', 'unbindable', 'make-unbindable', 'runbindable',
'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_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*"\
r"(?P<fstype>\(\s*(" + join_valid_fs + ")(" + sep + "(" + join_valid_fs + "))*\s*\)|"\
r"\{\s*(" + join_valid_fs + ")(" + sep + "(" + join_valid_fs + r"))*\s*\}|(\s*" + join_valid_fs + "))"\
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 + r')(' + sep + r'(' + join_valid_fs + r'))*\s*\)|'\
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*"\
r"(?P<options>\(\s*(" + join_valid_flags + ")(" + sep + "(" + join_valid_flags + r"))*\s*\)|" \
"(\s*" + join_valid_flags + ")"\
"))?"
mount_condition_pattern = rf"({fs_type_pattern})?\s*({option_pattern})?"
option_pattern = r'\s*(\boption(s?)\b\s*(?P<options_equals_or_in>=|in)\s*'\
r'(?P<options>\(\s*(' + join_valid_flags + r')(' + sep + r'(' + join_valid_flags + r'))*\s*\)|' \
r'(\s*' + join_valid_flags + r')'\
r'))?'
mount_condition_pattern = rf'({fs_type_pattern})?\s*({option_pattern})?'
# Source can either be
# - A path : /foo
# - A filesystem : sysfs (sudo mount -t tmpfs tmpfs /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+" + "))"
dest_fileglob_pattern = r"(\s*" + RE_PROFILE_PATH_OR_VAR % "dest_file" + ')'
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' + 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 to handle and store a single mount rule'''
@@ -91,22 +95,20 @@ class MountRule(BaseRule):
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')
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.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
if source != self.ALL and source[0].isalpha():
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)
self.source, self.all_source = self._aare_or_all(source, 'source', is_path=False, 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}')
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}')
if self.operation != 'mount' and not self.all_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
@classmethod
def _create_instance(cls, raw_rule, matches):
'''parse raw_rule and return instance of this class'''
@@ -132,7 +133,7 @@ class MountRule(BaseRule):
if matches.group('details'):
rule_details = matches.group('details')
if operation == "mount":
if operation == 'mount':
parsed = RE_MOUNT_DETAILS.search(rule_details)
else:
parsed = RE_UMOUNT_DETAILS.search(rule_details)
@@ -152,7 +153,7 @@ class MountRule(BaseRule):
is_options_equal = r['options_equals_or_in']
options = strip_parenthesis(r['options']).replace(',', ' ').split()
else:
is_options_equal =None
is_options_equal = None
options = cls.ALL
if operation == 'mount' and r['source_file'] is not None: # Umount cannot have a source
@@ -168,38 +169,42 @@ class MountRule(BaseRule):
else:
is_fstype_equal = None
is_options_equal = None
fstype = cls.ALL
fstype = cls.ALL
options = cls.ALL
source = cls.ALL
dest = cls.ALL
source = 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)
def get_clean(self, depth=0):
space = ' ' * depth
fstype = ' fstype%s(%s)' % (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 ''
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)' % (wrap_in_with_spaces(self.is_options_equal), ', '.join(sorted(self.options))) if not self.all_options else ''
source = ''
dest = ''
if self.operation == 'mount':
return ('%s%s%s%s%s%s%s,%s' % ( self.modifiers_str(),
space,
self.operation,
fstype,
options,
" " + str(self.source.regex) if not self.all_source else '',
" -> " + str(self.dest.regex) if not self.all_dest else '',
self.comment,
))
if not self.all_source:
source = ' ' + str(self.source.regex)
if not self.all_dest:
dest = ' -> ' + str(self.dest.regex)
else:
return ('%s%s%s%s%s%s,%s' % ( self.modifiers_str(),
space,
self.operation,
fstype,
options,
" " + str(self.dest.regex) if not self.all_dest else '',
self.comment,
))
if not self.all_dest:
dest = ' ' + str(self.dest.regex)
return ('%s%s%s%s%s%s%s,%s' % (self.modifiers_str(),
space,
self.operation,
fstype,
options,
source,
dest,
self.comment,
))
def _is_covered_localvars(self, other_rule):
if self.operation != other_rule.operation:
@@ -212,12 +217,7 @@ class MountRule(BaseRule):
return False
if not self._is_covered_list(self.options, self.all_options, other_rule.options, other_rule.all_options, 'options'):
return False
if not self.source_is_path and not other_rule.source_is_path:
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'):
if not self._is_covered_aare(self.source, self.all_source, other_rule.source, other_rule.all_source, 'source'):
return False
if not self._is_covered_aare(self.dest, self.all_dest, other_rule.dest, other_rule.all_dest, 'dest'):
return False
@@ -233,12 +233,7 @@ class MountRule(BaseRule):
return False
if self.fstype != rule_obj.fstype or self.options != rule_obj.options:
return False
if not self.source_is_path and not rule_obj.source_is_path:
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'):
if not self._is_equal_aare(self.source, self.all_source, rule_obj.source, rule_obj.all_source, 'source'):
return False
if not self._is_equal_aare(self.dest, self.all_dest, rule_obj.dest, rule_obj.all_dest, 'dest'):
return False
@@ -256,8 +251,8 @@ class MountRule(BaseRule):
elif not self.all_source and type(self.source) is not str:
self.source = self.source.glob_path()
if self.source.is_equal('/**/'):
self.all_source= True
self.source=self.ALL
self.all_source = True
self.source = self.ALL
else:
self.options = self.ALL
@@ -273,15 +268,22 @@ class MountRule(BaseRule):
return (
_('Operation'), operation,
_('Fstype'), (self.is_fstype_equal, fstype) if fstype != 'ALL' else fstype ,
_('Options'), (self.is_options_equal, options) if options != 'ALL' else options ,
_('Fstype'), (self.is_fstype_equal, fstype) if fstype != 'ALL' else fstype,
_('Options'), (self.is_options_equal, options) if options != 'ALL' else options,
_('Source'), source,
_('Destination'), dest,
)
class MountRuleset(BaseRuleset):
'''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

View File

@@ -28,36 +28,34 @@ _ = init_translation()
class MountTestParse(AATest):
tests = (
# 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) 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 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 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 @{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 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, #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 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=(ext3, ext4) option=(ro, rbind) /a -> /b, #cmt', MountRule('mount', ('=',('ext3', 'ext4')), ('=', ('ro', 'rbind')), "/a", "/b", False, False, False, ' #cmt')),
# 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) 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 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 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 @{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 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, #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 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=(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/,',
MountRule('mount', MountRule.ALL, ('=',('rw', 'rbind')), "/usr/lib{,32,64,x32}/modules/",
"/tmp/snap.rootfs_*{,/usr}/lib/modules/",
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 /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, '' )),
MountRule('mount', MountRule.ALL, ('=', ('rw', 'rbind')), '/usr/lib{,32,64,x32}/modules/',
'/tmp/snap.rootfs_*{,/usr}/lib/modules/',
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 /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, '' )),
)
def _run_test(self, rawrule, expected):
@@ -69,12 +67,12 @@ class MountTestParse(AATest):
class MountTestParseInvalid(AATest):
tests = (
('mount fstype=,', AppArmorException),
('mount fstype=(foo),', AppArmorException),
('mount fstype=(),', AppArmorException),
('mount options=(),', AppArmorException),
('mount option=(invalid),', AppArmorException),
('mount option=(ext3ext4),',AppArmorException),
('mount fstype=,', AppArmorException),
('mount fstype=(foo),', AppArmorException),
('mount fstype=(),', AppArmorException),
('mount options=(),', AppArmorException),
('mount option=(invalid),', AppArmorException),
('mount option=(ext3ext4),', AppArmorException),
)
def _run_test(self, rawrule, expected):
@@ -88,60 +86,69 @@ class MountTestParseInvalid(AATest):
def test_diff_non_mountrule(self):
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):
obj.is_equal(exp(False, False), False)
def test_diff_invalid_fstype_equals_or_in(self):
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):
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):
obj1 = MountRule('mount',("=", '(ext4)'), MountRule.ALL, MountRule.ALL, MountRule.ALL)
obj2 = MountRule('mount',MountRule.ALL, 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)
self.assertFalse(obj1.is_equal(obj2, False))
def test_diff_source(self):
obj1 = MountRule('mount',MountRule.ALL, MountRule.ALL, "/foo", MountRule.ALL)
obj2 = MountRule('mount',MountRule.ALL, MountRule.ALL, "/bar", MountRule.ALL)
obj1 = MountRule('mount', MountRule.ALL, MountRule.ALL, '/foo', MountRule.ALL)
obj2 = MountRule('mount', MountRule.ALL, MountRule.ALL, '/bar', MountRule.ALL)
self.assertFalse(obj1.is_equal(obj2, False))
def test_invalid_umount_with_source(self):
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):
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):
def test_fs(self):
with open("/proc/filesystems") as f:
with open('/proc/filesystems') as 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):
def test_glob(self):
globList = [(
"mount options=(bind, rw) /home/user/Downloads/ -> /mnt/a/,",
"mount options=(bind, rw) /home/user/Downloads/,",
"mount options=(bind, rw) /home/user/*/,",
"mount options=(bind, rw) /home/**/,",
"mount options=(bind, rw),",
"mount,",
"mount,",
'mount options=(bind, rw) /home/user/Downloads/ -> /mnt/a/,',
'mount options=(bind, rw) /home/user/Downloads/,',
'mount options=(bind, rw) /home/user/*/,',
'mount options=(bind, rw) /home/**/,',
'mount options=(bind, rw),',
'mount,',
'mount,',
)]
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.glob()
self.assertEqual(rule.get_clean(), globs[i+1])
self.assertEqual(rule.get_clean(), globs[i + 1])
class MountTestClean(AATest):
@@ -152,7 +159,13 @@ class MountTestClean(AATest):
(' mount fstype = ( sysfs , procfs ) , ', 'mount fstype=(procfs, sysfs),'),
(' mount options = ( rw ) , ', 'mount options=(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,'),
(' remount , ', 'remount,'),
(' remount /foo , ', 'remount /foo,'),
)
@@ -165,11 +178,12 @@ class MountTestClean(AATest):
self.assertEqual(expected, clean, 'unexpected clean rule')
self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule')
class MountLogprofHeaderTest(AATest):
tests = (
('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 fstype=(ext3, ext4) options=(ro, nosuid) /a -> /b,', [_('Operation'), _('mount'), _('Fstype'), ("=", _('ext3 ext4')),_('Options'), ("=", _('nosuid ro')),_('Source'), _('/a'), _('Destination'), _('/b')])
('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 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):
@@ -179,47 +193,45 @@ class MountLogprofHeaderTest(AATest):
class MountIsCoveredTest(AATest):
def test_is_covered(self):
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b*", "/b*")
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b*', '/b*')
tests = [
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b", "/bar"),
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/bar", "/b")
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b', '/bar'),
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/bar', '/b')
]
for test in tests:
self.assertTrue(obj.is_covered(MountRule(*test)))
self.assertFalse(obj.is_equal(MountRule(*test)))
def test_is_covered_fs_source(self):
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('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)))
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('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)))
def test_is_notcovered(self):
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/foo/b*", "/b*")
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/foo/b*', '/b*')
tests = [
("mount", ("in", ('ext3', 'ext4')), ("=", ('ro')), "/foo/bar", "/bar" ),
("mount", ("=", ('procfs, ext4')), ("=", ('ro')), "/foo/bar", "/bar" ),
("mount", ("=", ('ext3')), ("=", ('rw')), "/foo/bar", "/bar" ),
("mount", ("=", ('ext3', 'ext4')), MountRule.ALL, "/foo/b*", "/bar" ),
("mount", MountRule.ALL, ("=", ('ro')), "/foo/b*", "/bar" ),
("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "/invalid/bar", "/bar" ),
("umount", 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')), "/foo/b*", "/invalid" ),
('mount', ('in', ('ext3', 'ext4')), ('=', ('ro')), '/foo/bar', '/bar' ),
('mount', ('=', ('procfs', 'ext4')), ('=', ('ro')), '/foo/bar', '/bar' ),
('mount', ('=', ('ext3')), ('=', ('rw')), '/foo/bar', '/bar' ),
('mount', ('=', ('ext3', 'ext4')), MountRule.ALL, '/foo/b*', '/bar' ),
('mount', MountRule.ALL, ('=', ('ro')), '/foo/b*', '/bar' ),
('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), '/invalid/bar', '/bar' ),
('umount', 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')), '/foo/b*', '/invalid'),
]
for test in tests:
self.assertFalse(obj.is_covered(MountRule(*test)))
self.assertFalse(obj.is_equal(MountRule(*test)))
def test_is_not_covered_fs_source(self):
obj = MountRule("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "tmpfs", MountRule.ALL)
test = ("mount", ("=", ('ext3', 'ext4')), ("=", ('ro')), "procfs", MountRule.ALL)
obj = MountRule('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'tmpfs', MountRule.ALL)
test = ('mount', ('=', ('ext3', 'ext4')), ('=', ('ro')), 'procfs', MountRule.ALL)
self.assertFalse(obj.is_covered(MountRule(*test)))
self.assertFalse(obj.is_equal(MountRule(*test)))
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)

View File

@@ -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)

View File

@@ -85,6 +85,16 @@ exception_not_raised = (
'mount/bad_1.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_bad11.sd',
'profile/flags/flags_bad12.sd',
@@ -314,15 +324,8 @@ unknown_line = (
'bare_include_tests/ok_85.sd',
'bare_include_tests/ok_86.sd',
# option = make-${valid-option} (e.g. make-private) is not supported
'mount/ok_opt_48.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 fstype using AARE
'mount/ok_12.sd',
# Mount with flags in {remount, [r]unbindable, [r]shared, [r]private, [r]slave} does not support a source
'mount/ok_opt_68.sd',
@@ -334,15 +337,7 @@ unknown_line = (
'mount/ok_opt_74.sd',
'mount/ok_opt_75.sd',
# option = make-${valid-option} (e.g. make-private) is not supported
'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',
# options=slave with /** src (first rule in the test causes exception)
'mount/ok_opt_84.sd',
# 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_4.sd',
# Invalid keyword: read-only --> Should be ro
'mount/ok_opt_3.sd',
# Options should be comma separated
'mount/in_4.sd', # also order option then fstype is invalid
)

View File

@@ -14,7 +14,7 @@ import unittest
import apparmor.aa as aa
from apparmor.common import AppArmorBug, AppArmorException
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_match_include_parse, strip_parenthesis, strip_quotes)
from common_test import AATest, setup_aa, setup_all_loops
@@ -248,7 +248,7 @@ class AARegexMount(AARegexTest):
"""Tests for RE_PROFILE_MOUNT"""
def AASetup(self):
self.regex = aa.RE_PROFILE_MOUNT
self.regex = RE_PROFILE_MOUNT
tests = (
(' mount,', (None, None, 'mount,', 'mount', None, None)),