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

Compare commits

...

31 Commits

Author SHA1 Message Date
John Johansen
e5a3c03357 Prepare for AppArmor 3.1.4 release
- update version file
- update library version

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-05-23 17:10:26 -07:00
John Johansen
6831a8f440 Merge extend test profiles for mount
- in bad_?.sd, explain why the profile is bad (conflicting options)
- add a good profile with two space-separated options

This is a follow-up for https://gitlab.com/apparmor/apparmor/-/merge_requests/1029

If we backport !1029, we should also backport these test changes.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1035
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit d700f87d3e)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-05-23 14:41:58 -07:00
John Johansen
ea5b52d3b2 Merge aa-status: Fix malformed json output
In some cases (if profiles in complain _and_ enforce mode are loaded), the `i` loop runs more
than once, which also means `j == 0` is true in the middle of the json.
This causes invalid json.

This patch fixes this.

This is a regression related to 22aa9b6161
/ https://gitlab.com/apparmor/apparmor/-/merge_requests/964 /
https://gitlab.com/apparmor/apparmor/-/issues/295
which fixed another case of invalid json if a process was unconfined
while having a profile defined.

Note: I also tested this patch for the "unconfined, but has a profile
defined" case to ensure it doesn't break what
22aa9b6161 fixed.

This fix is needed in all branches that also got !964 (which means 3.1 and 3.0).

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1036
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/295
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
backported-from: 88d2bf45a Merge aa-status: Fix malformed json output
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-05-19 16:31:41 -07:00
Christian Boltz
3cdeb140a1 Merge nscd: add permission to allow supporting unscd
`unscd` is a drop-in replacement for `nscd` that uses the same binary location (`/usr/sbin/nscd`) and config file (`/etc/nscd.conf`). The `usr.sbin.nscd` profile only needs one additional permission to support it.

```
May 9 18:07:42 darkstar kernel: [ 2706.138823] audit: type=1400
audit(1683670062.580:839): apparmor="DENIED" operation="sendmsg"
profile="nscd" name="/run/systemd/notify" pid=4343 comm="nscd"
requested_mask="w" denied_mask="w" fsuid=125 ouid=0
```

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1031
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>


(cherry picked from commit dec3815f07)

bd0d401b nscd: add permission to allow supporting unscd
2023-05-10 10:54:34 +00:00
Jon Tourville
8c49449e46 Merge Merge Issue 312: added missing kernel mount options
This patch adds the following mount options: 'nostrictatime',
'lazytime', and 'nolazytime'.

The MS_STRICTATIME mount flag already existed, and 'nostrictatime' was
listed along with 'strictatime' in the comments of parser/mount.cc, so
this patch adds a mapping for 'nostrictatime' to clear MS_STRICTATIME.

Additionally, the Linux kernel includes the 'lazytime' option with
MS_LAZYTIME mapping to (1<<25), so this patch adds MS_LAZYTIME to
parser/mount.h and the corresponding mappings in parser/mount.cc for
'lazytime' and 'nolazytime'.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1005
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit c37be61d17)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1026
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Jon Tourville <jon.tourville@canonical.com>
2023-05-04 12:00:20 +00:00
Christian Boltz
a3685436eb Merge [3.1] CI: check extra profiles for local/ includes
Picked from MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1012
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>


(cherry picked from commit 46c8dbe886)

3ddb1677 CI: check extra profiles for local/ includes

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1027
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2023-05-04 10:33:57 +00:00
John Johansen
2610fe4e80 Merge CI: check extra profiles for local/ includes
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1012
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>


(cherry picked from commit 46c8dbe886)

3ddb1677 CI: check extra profiles for local/ includes
2023-05-03 18:15:19 +00:00
John Johansen
c7f761b710 Merge Issue 312: added missing kernel mount options
This patch adds the following mount options: 'nostrictatime',
'lazytime', and 'nolazytime'.

The MS_STRICTATIME mount flag already existed, and 'nostrictatime' was
listed along with 'strictatime' in the comments of parser/mount.cc, so
this patch adds a mapping for 'nostrictatime' to clear MS_STRICTATIME.

Additionally, the Linux kernel includes the 'lazytime' option with
MS_LAZYTIME mapping to (1<<25), so this patch adds MS_LAZYTIME to
parser/mount.h and the corresponding mappings in parser/mount.cc for
'lazytime' and 'nolazytime'.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1005
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit c37be61d17)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
2023-05-03 17:00:15 +02:00
Jon Tourville
f00f296201 Merge Merge expand mount tests
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1006
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit e6e5e7981f)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1022
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Jon Tourville <jon.tourville@canonical.com>
2023-05-03 14:54:32 +00:00
Jon Tourville
83772acf00 Check for newer mount options in regression test
The mount options MS_LAZYTIME and MS_NOSYMFOLLOW were added in
kernels 4.0 and 5.10, respectively. Update the mount test script
and helper to skip testing those options if they are not available.

Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
(cherry picked from commit 9a760def8d)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
2023-05-03 13:48:02 +02:00
John Johansen
1d35eedef8 Merge Support rule qualifiers in regression tests
This allows regression tests to generate profiles that use rule qualifiers,
such as allow, deny, and audit. Qualifiers can be specified for a rule by
prepending 'qual=', followed by a comma-separated list of rule qualifiers,
then a ':', then the rule itself.

Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
(cherry picked from commit f6bfd141bd)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1018
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
2023-05-02 12:00:13 +00:00
John Johansen
a597a612a7 Merge expand mount tests
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1006
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit e6e5e7981f)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
2023-05-02 11:01:12 +02:00
Jon Tourville
64463c8686 Support rule qualifiers in regression tests
This allows regression tests to generate profiles that use rule qualifiers,
such as allow, deny, and audit. Qualifiers can be specified for a rule by
prepending 'qual=', followed by a comma-separated list of rule qualifiers,
then a ':', then the rule itself.

Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
(cherry picked from commit f6bfd141bd)
Signed-off-by: Jon Tourville <jon.tourville@canonical.com>
2023-05-02 10:12:23 +02:00
John Johansen
7bd1c4d8ef Merge fix af_unix tests for v8 networking.
The unix network tests are not being run on a v8 network capable kernel. Under v8 there needs to be some adjustments to the tests because unix rules get downgraded to the socket rule ```network unix,``` which does not have the same set of conditionals or fine grained permissions, meaning some tests that would fail under af_unix (like missing permission tests) will pass under v8 network rules.

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/893
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 59b4109a8b)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-05-01 05:17:20 -07:00
John Johansen
c7bd872071 Merge abstractions/freedesktop.org: allow custom cursors
... by allowing to read all files below ~/.icons instead of only the
directory listing.

I propose this patch for all branches.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1008
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>


(cherry picked from commit fa86a7f1d9)

954b11fc abstractions/freedesktop.org: allow custom cursors
2023-04-25 19:44:27 +00:00
John Johansen
3d33fd440b Merge Fix error when choosing named exec with plane profile names
When a user choooses to execute to a named profile (not: named child),
make sure to get the profile filename in the correct way to avoid a crash.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/314

I propose this patch for 2.13..master. (Note: I verified that the bug already exists in 2.13, and that this patch fixes it.)

Closes #314
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1013
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>


(cherry picked from commit 30dd1cec42)

103be8e1 Fix error when choosing named exec with plane profile names
2023-04-25 19:42:26 +00:00
Christian Boltz
e37cbd43c9 Merge Sync apparmor-3.1 profiles with master
This MR is similar to !1009, only it targets the `apparmor-3.1` branch.

There are also three commits here, but the first one only updates comments, and the last one is much smaller. After merging, `git diff apparmor-3.1 master -- profiles` will likewise produce no output.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1010
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2023-04-19 20:21:58 +00:00
Daniel Richard G
f91f33f2c6 profiles: sync with master
(Don't remove rules from the usr.sbin.{dnsmasq,nmbd,winbindd} profiles,
however, as these have been transferred to abstractions which might not
be correctly updated on a user system)
2023-04-18 20:24:32 -04:00
Daniel Richard G
51751aa687 profiles/Makefile: sync with master 2023-04-12 20:48:57 -04:00
Daniel Richard G
89f1a84dfe profiles: partial sync with master
This commit only updates comments.
2023-04-12 20:48:57 -04:00
Christian Boltz
d984b3edb6 Merge abstractions/base: allow reading tzdata ICU zoneinfo DB
This is used by various applications including libreoffice etc so it may as well
be added to the base abstraction along with the existing zoneinfo DB access.

AVC apparmor="DENIED" operation="open" class="file" profile="snap.libreoffice.calc" name="/usr/share/zoneinfo-icu/44/le/zoneinfo64.res" pid=44742 comm="soffice.bin" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0

Signed-off-by: Alex Murray <alex.murray@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1007
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>


(cherry picked from commit 8d9985ac0b)

c691b11d abstractions/base: allow reading tzdata ICU zoneinfo DB
2023-04-11 19:21:29 +00:00
John Johansen
2a7ddbc773 Merge Ignore 'x' in mixed file mode log events
Probably thanks to O_MAYEXEC, denials for file access can now contain a
mix of x (exec) and other file permissions.

The actual exec should appear in a separate "exec" log event, therefore
ignore 'x' in file events for now if it's mixed with other permissions.

Note that file events ("open", "link" etc.) that contain denied_mask="x"
without another permission will still cause an error. (So far, this
hasn't been seen in the wild.)

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/303

Also add the log line from the bugreport and the (for now) expected
result as test_multi testcase.

I propose this patch for all branches.

Closes #303
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1001
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit cf6539b217)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-03-31 20:14:17 -07:00
John Johansen
54bb7dccf0 Merge syslogd: allow reading /dev/kmsg
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/307

I propose this patch for master and 3.x (the profile in 2.13 is very different which makes automatic merging impossible)

Closes #307
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1003
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 6f65faa164)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-03-31 20:08:12 -07:00
John Johansen
aff29ef0ee Merge Fix mount rules encoding
This is a partial fix for CVE-2016-1585, it address the frontend rule encoding problems particularly
- Permissions being given that shouldn't happen
- Multiple option conditionals in a single rule resulting in wider permission instead of multiple rules
- optional flags not being handled correctly
- multiple backend rules being created out of one frontend rule when they shouldn't be

it does not address the backend issue of short cut permissions not being correctly updated when deny rules carve out permissions on an allow rule that has a short cut permission in the encoding.

Thanks to the additional work by Alexander Mikhalitsyn for beating this MR into shape so we can land it

Alexander Changelog:
- rebased to an actual tree
- addressed review comments from @wbumiller and @setharnold
- fixed compiler warnings about class_mount_hdr is uninitialized
- infinite loop fix
- MS_MAKE_CMDS bitmask value fixed
- fixed condition in `gen_flag_rules` to cover cases like `mount options in (bind) /d -> /4,` when flags are empty and only opt_flags are present
- marked some tests as a FAIL case behavior was changed after `parser: add conflicting flags check for options= conditionals` commit

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/333
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit c1a1a3a923)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-03-29 10:41:47 -07:00
John Johansen
26e85c17bd Merge parser: fix definitely and possibly lost memory leaks
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/992
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 05595eccda)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-03-28 20:21:09 -07:00
Georgia Garcia
bdcd76a8ea Merge tests: force dbus-daemon to generate an abstract socket
dbus 1.14.4 changed the behavior of unix:tmpdir to be equivalent to
unix:dir, which cases dbus-daemon to generate path based sockets,
instead of the previous abstract sockets. [1]
In this change we force dbus-daemon to generate an abstract socket by
specifying the abstract socket address in the command.

[1] https://gitlab.freedesktop.org/dbus/dbus/-/blob/dbus-1.14/NEWS#L64

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/999
Approved-by: John Johansen <john@jjmx.net>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>


(cherry picked from commit 904b733948)

53d4e341 tests: force dbus-daemon to generate an abstract socket
2023-03-28 14:23:13 +00:00
Christian Boltz
e69cb50479 Merge [3.x] several fixes for samba-related profiles and the kerberos abstraction
See the individual commits for details.

Signed-off-by: Noel Power <noel.power@suse.com>

This is a backport of https://gitlab.com/apparmor/apparmor/-/merge_requests/989

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/991
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>
2023-03-14 20:22:28 +00:00
Noel Power
5a25bc6240 prevent rename_src requesting 'r' access DENIES messages
nmbd, windbind & smbd all trigger

apparmor="DENIED" operation="rename_src" profile="/usr/sbin/nmbd" name="/var/log/samba/log.nmbd

type messages.

Signed-off-by: Noel Power <noel.power@suse.com>
(cherry picked from commit e5654f1f81)
2023-03-14 21:08:39 +01:00
Noel Power
06e15a7789 add kerberosclient to included abstractions for winbindd
prevent messages like

apparmor="DENIED" operation="file_mmap" profile="/usr/sbin/winbindd" name="/usr/lib64/krb5/plugins/authdata/sssd_pac_plugin.so" pid=2798 comm="winbindd" requested_mask="m" denied_mask="m" fsuid=52311 ouid=0

Signed-off-by: Noel Power <noel.power@suse.com>
(cherry picked from commit 0593a035f5)
2023-03-14 21:08:33 +01:00
Noel Power
d0e086e93a Update kerberosclient abstraction for access to authdata directory
For example winbindd when configured on a samba system using
sssd can trigger

apparmor="DENIED" operation="file_mmap" profile="/usr/sbin/winbindd" name="/usr/lib64/krb5/plugins/authdata /sssd_pac_plugin.so" pid=2798 comm="winbindd" requested_mask="m" denied_mask="m" fsuid=52311 ouid=0

Signed-off-by: Noel Power <noel.power@suse.com>
(cherry picked from commit 6e94794c68)
2023-03-14 21:08:28 +01:00
Noel Power
0e6b48cc78 adjust winbindd profile to cater for sssd kdcinfo access
winbindd (with nsswitch sssd configuration) is now getting

type=AVC msg=audit(1677832823.657:119): apparmor="DENIED" operation="open" profile="/usr/sbin/winbindd" name="/var/lib/sss/pubconf/kdcinfo.TESTDOMAIN1.MY.COM" pid=3026 comm="winbindd" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

Signed-off-by: Noel Power <noel.power@suse.com>
(cherry picked from commit b4f5414882)
2023-03-14 21:08:10 +01:00
75 changed files with 1293 additions and 445 deletions

View File

@@ -104,6 +104,7 @@ test-profiles:
script:
- make -C profiles check-parser
- make -C profiles check-abstractions.d
- make -C profiles check-extras
shellcheck:
stage: test

View File

@@ -454,6 +454,7 @@ static int detailed_output(FILE *json) {
const char *process_statuses[] = {"enforce", "complain", "unconfined", "mixed", "kill"};
int ret;
size_t i;
int need_finish = 0;
ret = get_profiles(&profiles, &nprofiles);
if (ret != 0) {
@@ -534,19 +535,20 @@ static int detailed_output(FILE *json) {
} else {
fprintf(json, "%s\"%s\": [{\"profile\": \"%s\", \"pid\": \"%s\", \"status\": \"%s\"}",
// first element will be a unique executable
j == 0 ? "" : "], ",
j == 0 && !need_finish ? "" : "], ",
filtered[j].exe, filtered[j].profile, filtered[j].pid, filtered[j].mode);
}
}
if (j > 0) {
fprintf(json, "]");
need_finish = 1;
}
}
free_processes(filtered, nfiltered);
}
if (json) {
fprintf(json, "}}\n");
if (need_finish > 0) {
fprintf(json, "]");
}
fprintf(json, "}\n");
}
exit:

View File

@@ -1 +1 @@
3.1.3
3.1.4

View File

@@ -33,9 +33,9 @@ INCLUDES = $(all_includes)
# After changing the AA_LIB_* variables, also update EXPECTED_SO_NAME.
AA_LIB_CURRENT = 13
AA_LIB_REVISION = 2
AA_LIB_REVISION = 3
AA_LIB_AGE = 12
EXPECTED_SO_NAME = libapparmor.so.1.12.2
EXPECTED_SO_NAME = libapparmor.so.1.12.3
SUFFIXES = .pc.in .pc

View File

@@ -0,0 +1 @@
type=AVC msg=audit(1676978994.840:1493): apparmor="DENIED" operation="link" profile="cargo" name="/var/tmp/portage/dev-lang/rust-1.67.1/work/rustc-1.67.1-src/build/bootstrap/debug/libbootstrap.rlib" pid=12412 comm="cargo" requested_mask="xm" denied_mask="xm" fsuid=250 ouid=250 target="/var/tmp/portage/dev-lang/rust-1.67.1/work/rustc-1.67.1-src/build/bootstrap/debug/deps/libbootstrap-4542dd99e796257e.rlib"FSUID="portage" OUID="portage"

View File

@@ -0,0 +1,16 @@
START
File: file_xm.in
Event type: AA_RECORD_DENIED
Audit ID: 1676978994.840:1493
Operation: link
Mask: xm
Denied Mask: xm
fsuid: 250
ouid: 250
Profile: cargo
Name: /var/tmp/portage/dev-lang/rust-1.67.1/work/rustc-1.67.1-src/build/bootstrap/debug/libbootstrap.rlib
Command: cargo
Name2: /var/tmp/portage/dev-lang/rust-1.67.1/work/rustc-1.67.1-src/build/bootstrap/debug/deps/libbootstrap-4542dd99e796257e.rlib
PID: 12412
Epoch: 1676978994
Audit subid: 1493

View File

@@ -0,0 +1,4 @@
profile cargo {
owner /var/tmp/portage/dev-lang/rust-1.67.1/work/rustc-1.67.1-src/build/bootstrap/debug/libbootstrap.rlib m,
}

View File

@@ -172,7 +172,7 @@ B<MOUNT FLAGS EXPRESSION> = ( I<MOUNT FLAGS LIST> | I<MOUNT EXPRESSION> )
B<MOUNT FLAGS LIST> = Comma separated list of I<MOUNT FLAGS>.
B<MOUNT FLAGS> = ( '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' | 'nouser' | 'user' )
B<MOUNT FLAGS> = ( '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' )
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...

View File

@@ -98,6 +98,9 @@
* nomand
* #define MS_DIRSYNC 128 Directory modifications are synchronous
* dirsync
* #define MS_NOSYMFOLLOW 256 Do not follow symlinks
* symfollow
* nosymfollow
* #define MS_NOATIME 1024 Do not update access times
* noatime
* atime
@@ -139,6 +142,9 @@
* #define MS_STRICTATIME (1<<24) Always perform atime updates
* strictatime
* nostrictatime
* #define MS_LAZYTIME (1<<25) Update the on-disk [acm]times lazily
* lazytime
* nolazytime
* #define MS_NOSEC (1<<28)
* #define MS_BORN (1<<29)
* #define MS_ACTIVE (1<<30)
@@ -246,6 +252,8 @@ static struct mnt_keyword_table mnt_opts_table[] = {
{"mand", MS_MAND, 0},
{"nomand", 0, MS_MAND},
{"dirsync", MS_DIRSYNC, 0},
{"symfollow", 0, MS_NOSYMFOLLOW},
{"nosymfollow", MS_NOSYMFOLLOW, 0},
{"atime", 0, MS_NOATIME},
{"noatime", MS_NOATIME, 0},
{"diratime", 0, MS_NODIRATIME},
@@ -283,6 +291,9 @@ static struct mnt_keyword_table mnt_opts_table[] = {
{"iversion", MS_IVERSION, 0},
{"noiversion", 0, MS_IVERSION},
{"strictatime", MS_STRICTATIME, 0},
{"nostrictatime", 0, MS_STRICTATIME},
{"lazytime", MS_LAZYTIME, 0},
{"nolazytime", 0, MS_LAZYTIME},
{"user", 0, (unsigned int) MS_NOUSER},
{"nouser", (unsigned int) MS_NOUSER, 0},
@@ -298,6 +309,22 @@ static struct mnt_keyword_table mnt_conds_table[] = {
{NULL, 0, 0}
};
static ostream &dump_flags(ostream &os,
pair <unsigned int, unsigned int> flags)
{
for (int i = 0; mnt_opts_table[i].keyword; i++) {
if ((flags.first & mnt_opts_table[i].set) ||
(flags.second & mnt_opts_table[i].clear))
os << mnt_opts_table[i].keyword;
}
return os;
}
ostream &operator<<(ostream &os, pair<unsigned int, unsigned int> flags)
{
return dump_flags(os, flags);
}
static int find_mnt_keyword(struct mnt_keyword_table *table, const char *name)
{
int i;
@@ -320,7 +347,7 @@ int is_valid_mnt_cond(const char *name, int src)
static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
{
unsigned int flags = 0;
unsigned int flags = 0, invflags = 0;
*inv = 0;
struct value_list *entry, *tmp, *prev = NULL;
@@ -329,11 +356,11 @@ static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
i = find_mnt_keyword(mnt_opts_table, entry->value);
if (i != -1) {
flags |= mnt_opts_table[i].set;
*inv |= mnt_opts_table[i].clear;
invflags |= mnt_opts_table[i].clear;
PDEBUG(" extracting mount flag %s req: 0x%x inv: 0x%x"
" => req: 0x%x inv: 0x%x\n",
entry->value, mnt_opts_table[i].set,
mnt_opts_table[i].clear, flags, *inv);
mnt_opts_table[i].clear, flags, invflags);
if (prev)
prev->next = tmp;
if (entry == *list)
@@ -344,9 +371,27 @@ static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
prev = entry;
}
if (inv)
*inv = invflags;
return flags;
}
static bool conflicting_flags(unsigned int flags, unsigned int inv)
{
if (flags & inv) {
for (int i = 0; i < 31; i++) {
unsigned int mask = 1 << i;
if ((flags & inv) & mask) {
cerr << "conflicting flag values = "
<< flags << ", " << inv << "\n";
}
}
return true;
}
return false;
}
static struct value_list *extract_fstype(struct cond_entry **conds)
{
struct value_list *list = NULL;
@@ -369,22 +414,19 @@ static struct value_list *extract_fstype(struct cond_entry **conds)
return list;
}
static struct value_list *extract_options(struct cond_entry **conds, int eq)
static struct cond_entry *extract_options(struct cond_entry **conds, int eq)
{
struct value_list *list = NULL;
struct cond_entry *entry, *tmp, *prev = NULL;
struct cond_entry *list = NULL, *entry, *tmp, *prev = NULL;
list_for_each_safe(*conds, entry, tmp) {
if ((strcmp(entry->name, "options") == 0 ||
strcmp(entry->name, "option") == 0) &&
entry->eq == eq) {
list_remove_at(*conds, prev, entry);
PDEBUG(" extracting option %s\n", entry->name);
list_append(entry->vals, list);
list = entry->vals;
entry->vals = NULL;
free_cond_entry(entry);
PDEBUG(" extracting %s %s\n", entry->name, entry->eq ?
"=" : "in");
list_append(entry, list);
list = entry;
} else
prev = entry;
}
@@ -392,60 +434,129 @@ static struct value_list *extract_options(struct cond_entry **conds, int eq)
return list;
}
static void perror_conds(const char *rule, struct cond_entry *conds)
{
struct cond_entry *entry;
list_for_each(conds, entry) {
PERROR( "unsupported %s condition '%s%s(...)'\n", rule, entry->name, entry->eq ? "=" : " in ");
}
}
static void perror_vals(const char *rule, struct value_list *vals)
{
struct value_list *entry;
list_for_each(vals, entry) {
PERROR( "unsupported %s value '%s'\n", rule, entry->value);
}
}
static void process_one_option(struct cond_entry *&opts, unsigned int &flags,
unsigned int &inv_flags)
{
struct cond_entry *entry;
struct value_list *vals;
entry = list_pop(opts);
vals = entry->vals;
entry->vals = NULL;
/* fail if there are any unknown optional flags */
if (opts) {
PERROR(" unsupported multiple 'mount options %s(...)'\n", entry->eq ? "=" : " in ");
exit(1);
}
free_cond_entry(entry);
flags = extract_flags(&vals, &inv_flags);
if (vals) {
perror_vals("mount option", vals);
exit(1);
}
}
mnt_rule::mnt_rule(struct cond_entry *src_conds, char *device_p,
struct cond_entry *dst_conds unused, char *mnt_point_p,
int allow_p):
mnt_point(mnt_point_p), device(device_p), trans(NULL), opts(NULL),
flags(0), inv_flags(0), audit(0), deny(0)
flagsv(0), opt_flagsv(0), audit(0), deny(0)
{
/* FIXME: dst_conds are ignored atm */
dev_type = extract_fstype(&src_conds);
if (src_conds) {
struct value_list *list = extract_options(&src_conds, 0);
/* move options in () to local list */
struct cond_entry *opts_in = extract_options(&src_conds, 0);
opts = extract_options(&src_conds, 1);
if (opts)
flags = extract_flags(&opts, &inv_flags);
if (opts_in) {
unsigned int tmpflags = 0, tmpinv_flags = 0;
struct cond_entry *entry;
if (list) {
unsigned int tmpflags, tmpinv_flags = 0;
tmpflags = extract_flags(&list, &tmpinv_flags);
/* these flags are optional so set both */
tmpflags |= tmpinv_flags;
tmpinv_flags |= tmpflags;
flags |= tmpflags;
inv_flags |= tmpinv_flags;
if (opts)
list_append(opts, list);
else if (list)
opts = list;
while ((entry = list_pop(opts_in))) {
process_one_option(entry, tmpflags,
tmpinv_flags);
/* optional flags if set/clear mean the same
* thing and can be represented by a single
* bitset, also there is no need to check for
* conflicting flags when they are optional
*/
opt_flagsv.push_back(tmpflags | tmpinv_flags);
}
}
/* move options=() to opts list */
struct cond_entry *opts_eq = extract_options(&src_conds, 1);
if (opts_eq) {
unsigned int tmpflags = 0, tmpinv_flags = 0;
struct cond_entry *entry;
while ((entry = list_pop(opts_eq))) {
process_one_option(entry, tmpflags,
tmpinv_flags);
/* throw away tmpinv_flags, only needed in
* consistancy check
*/
if (allow_p & AA_DUMMY_REMOUNT)
tmpflags |= MS_REMOUNT;
if (conflicting_flags(tmpflags, tmpinv_flags)) {
PERROR("conflicting flags in the rule\n");
exit(1);
}
flagsv.push_back(tmpflags);
}
}
if (src_conds) {
perror_conds("mount", src_conds);
exit(1);
}
}
if (!(flagsv.size() + opt_flagsv.size())) {
/* no flag options, and not remount, allow everything */
if (allow_p & AA_DUMMY_REMOUNT) {
flagsv.push_back(MS_REMOUNT);
opt_flagsv.push_back(MS_REMOUNT_FLAGS & ~MS_REMOUNT);
} else {
flagsv.push_back(MS_ALL_FLAGS);
opt_flagsv.push_back(MS_ALL_FLAGS);
}
} else if (!(flagsv.size())) {
/* no flags but opts set */
if (allow_p & AA_DUMMY_REMOUNT)
flagsv.push_back(MS_REMOUNT);
else
flagsv.push_back(0);
} else if (!(opt_flagsv.size())) {
opt_flagsv.push_back(0);
}
if (allow_p & AA_DUMMY_REMOUNT) {
allow_p = AA_MAY_MOUNT;
flags |= MS_REMOUNT;
inv_flags = 0;
} else if (!(flags | inv_flags)) {
/* no flag options, and not remount, allow everything */
flags = MS_ALL_FLAGS;
inv_flags = MS_ALL_FLAGS;
}
allow = allow_p;
if (src_conds) {
PERROR(" unsupported mount conditions\n");
exit(1);
}
if (opts) {
PERROR(" unsupported mount options\n");
exit(1);
}
}
ostream &mnt_rule::dump(ostream &os)
@@ -459,7 +570,11 @@ ostream &mnt_rule::dump(ostream &os)
else
os << "error: unknown mount perm";
os << " (0x" << hex << flags << " - 0x" << inv_flags << ") ";
for (unsigned int i = 0; i < flagsv.size(); i++)
os << " flags=(0x" << hex << flagsv[i] << ")";
for (unsigned int i = 0; i < opt_flagsv.size(); i++)
os << " flags in (0x" << hex << opt_flagsv[i] << ")";
if (dev_type) {
os << " type=";
print_value_list(dev_type);
@@ -515,7 +630,7 @@ int mnt_rule::expand_variables(void)
}
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
unsigned int inv_flags)
unsigned int opt_flags)
{
char *p = buffer;
int i, len = 0;
@@ -528,7 +643,7 @@ static int build_mnt_flags(char *buffer, int size, unsigned int flags,
return TRUE;
}
for (i = 0; i <= 31; ++i) {
if ((flags & inv_flags) & (1 << i))
if ((opt_flags) & (1 << i))
len = snprintf(p, size, "(\\x%02x|)", i + 1);
else if (flags & (1 << i))
len = snprintf(p, size, "\\x%02x", i + 1);
@@ -583,7 +698,9 @@ void mnt_rule::warn_once(const char *name)
rule_t::warn_once(name, "mount rules not enforce");
}
int mnt_rule::gen_policy_re(Profile &prof)
int mnt_rule::gen_policy_remount(Profile &prof, int &count,
unsigned int flags, unsigned int opt_flags)
{
std::string mntbuf;
std::string devbuf;
@@ -592,8 +709,307 @@ int mnt_rule::gen_policy_re(Profile &prof)
std::string optsbuf;
char class_mount_hdr[64];
const char *vec[5];
int tmpallow;
sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT);
/* remount can't be conditional on device and type */
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (mnt_point) {
/* both device && mnt_point or just mnt_point */
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
} else {
if (!convert_entry(mntbuf, device))
goto fail;
vec[0] = mntbuf.c_str();
}
/* skip device */
vec[1] = default_match_pattern;
/* skip type */
vec[2] = default_match_pattern;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags & MS_REMOUNT_FLAGS,
opt_flags & MS_REMOUNT_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (opts)
tmpallow = AA_MATCH_CONT;
else
tmpallow = allow;
/* rule for match without required data || data MATCH_CONT */
if (!prof.policy.rules->add_rule_vec(deny, tmpallow,
audit | AA_AUDIT_MNT_DATA, 4,
vec, dfaflags, false))
goto fail;
count++;
if (opts) {
/* rule with data match required */
optsbuf.clear();
if (!build_mnt_opts(optsbuf, opts))
goto fail;
vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(deny, allow,
audit | AA_AUDIT_MNT_DATA,
5, vec, dfaflags, false))
goto fail;
count++;
}
return RULE_OK;
fail:
return RULE_ERROR;
}
int mnt_rule::gen_policy_bind_mount(Profile &prof, int &count,
unsigned int flags, unsigned int opt_flags)
{
std::string mntbuf;
std::string devbuf;
std::string typebuf;
char flagsbuf[PATH_MAX + 3];
std::string optsbuf;
char class_mount_hdr[64];
const char *vec[5];
sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT);
/* bind mount rules can't be conditional on dev_type or data */
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
if (!clear_and_convert_entry(devbuf, device))
goto fail;
vec[1] = devbuf.c_str();
/* skip type */
vec[2] = default_match_pattern;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags & MS_BIND_FLAGS,
opt_flags & MS_BIND_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(deny, allow, audit, 4, vec,
dfaflags, false))
goto fail;
count++;
return RULE_OK;
fail:
return RULE_ERROR;
}
int mnt_rule::gen_policy_change_mount_type(Profile &prof, int &count,
unsigned int flags,
unsigned int opt_flags)
{
std::string mntbuf;
std::string devbuf;
std::string typebuf;
char flagsbuf[PATH_MAX + 3];
std::string optsbuf;
char class_mount_hdr[64];
const char *vec[5];
sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT);
/* change type base rules can not be conditional on device,
* device type or data
*/
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
/* skip device and type */
vec[1] = default_match_pattern;
vec[2] = default_match_pattern;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags & MS_MAKE_FLAGS,
opt_flags & MS_MAKE_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(deny, allow, audit, 4, vec,
dfaflags, false))
goto fail;
count++;
return RULE_OK;
fail:
return RULE_ERROR;
}
int mnt_rule::gen_policy_move_mount(Profile &prof, int &count,
unsigned int flags, unsigned int opt_flags)
{
std::string mntbuf;
std::string devbuf;
std::string typebuf;
char flagsbuf[PATH_MAX + 3];
std::string optsbuf;
char class_mount_hdr[64];
const char *vec[5];
sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT);
/* mount move rules can not be conditional on dev_type,
* or data
*/
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
if (!clear_and_convert_entry(devbuf, device))
goto fail;
vec[1] = devbuf.c_str();
/* skip type */
vec[2] = default_match_pattern;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags & MS_MOVE_FLAGS,
opt_flags & MS_MOVE_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(deny, allow, audit, 4, vec,
dfaflags, false))
goto fail;
count++;
return RULE_OK;
fail:
return RULE_ERROR;
}
int mnt_rule::gen_policy_new_mount(Profile &prof, int &count,
unsigned int flags, unsigned int opt_flags)
{
std::string mntbuf;
std::string devbuf;
std::string typebuf;
char flagsbuf[PATH_MAX + 3];
std::string optsbuf;
char class_mount_hdr[64];
const char *vec[5];
int tmpallow;
sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT);
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
if (!clear_and_convert_entry(devbuf, device))
goto fail;
vec[1] = devbuf.c_str();
typebuf.clear();
if (!build_list_val_expr(typebuf, dev_type))
goto fail;
vec[2] = typebuf.c_str();
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags & MS_NEW_FLAGS,
opt_flags & MS_NEW_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (opts)
tmpallow = AA_MATCH_CONT;
else
tmpallow = allow;
/* rule for match without required data || data MATCH_CONT */
if (!prof.policy.rules->add_rule_vec(deny, tmpallow,
audit | AA_AUDIT_MNT_DATA, 4,
vec, dfaflags, false))
goto fail;
count++;
if (opts) {
/* rule with data match required */
optsbuf.clear();
if (!build_mnt_opts(optsbuf, opts))
goto fail;
vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(deny, allow,
audit | AA_AUDIT_MNT_DATA,
5, vec, dfaflags, false))
goto fail;
count++;
}
return RULE_OK;
fail:
return RULE_ERROR;
}
int mnt_rule::gen_flag_rules(Profile &prof, int &count, unsigned int flags,
unsigned int opt_flags)
{
/*
* XXX: added !flags to cover cases like:
* mount options in (bind) /d -> /4,
*/
if ((allow & AA_MAY_MOUNT) && (!flags || flags == MS_ALL_FLAGS)) {
/* no mount flags specified, generate multiple rules */
if (!device && !dev_type &&
gen_policy_remount(prof, count, flags, opt_flags) == RULE_ERROR)
return RULE_ERROR;
if (!dev_type && !opts &&
gen_policy_bind_mount(prof, count, flags, opt_flags) == RULE_ERROR)
return RULE_ERROR;
if (!device && !dev_type && !opts &&
gen_policy_change_mount_type(prof, count, flags, opt_flags) == RULE_ERROR)
return RULE_ERROR;
if (!dev_type && !opts &&
gen_policy_move_mount(prof, count, flags, opt_flags) == RULE_ERROR)
return RULE_ERROR;
return gen_policy_new_mount(prof, count, flags, opt_flags);
} else if ((allow & AA_MAY_MOUNT) && (flags & MS_REMOUNT)
&& !device && !dev_type) {
return gen_policy_remount(prof, count, flags, opt_flags);
} else if ((allow & AA_MAY_MOUNT) && (flags & MS_BIND)
&& !dev_type && !opts) {
return gen_policy_bind_mount(prof, count, flags, opt_flags);
} else if ((allow & AA_MAY_MOUNT) &&
(flags & (MS_MAKE_CMDS))
&& !device && !dev_type && !opts) {
return gen_policy_change_mount_type(prof, count, flags, opt_flags);
} else if ((allow & AA_MAY_MOUNT) && (flags & MS_MOVE)
&& !dev_type && !opts) {
return gen_policy_move_mount(prof, count, flags, opt_flags);
} else if ((allow & AA_MAY_MOUNT) &&
((flags | opt_flags) & ~MS_CMDS)) {
/* generic mount if flags are set that are not covered by
* above commands
*/
return gen_policy_new_mount(prof, count, flags, opt_flags);
} /* else must be RULE_OK for some rules */
return RULE_OK;
}
int mnt_rule::gen_policy_re(Profile &prof)
{
std::string mntbuf;
std::string devbuf;
std::string typebuf;
std::string optsbuf;
char class_mount_hdr[64];
const char *vec[5];
int count = 0;
unsigned int tmpflags, tmpinv_flags;
if (!features_supports_mount) {
warn_once(prof.name);
@@ -605,202 +1021,10 @@ int mnt_rule::gen_policy_re(Profile &prof)
/* a single mount rule may result in multiple matching rules being
* created in the backend to cover all the possible choices
*/
if ((allow & AA_MAY_MOUNT) && (flags & MS_REMOUNT)
&& !device && !dev_type) {
int tmpallow;
/* remount can't be conditional on device and type */
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (mnt_point) {
/* both device && mnt_point or just mnt_point */
if (!convert_entry(mntbuf, mnt_point))
for (size_t i = 0; i < flagsv.size(); i++) {
for (size_t j = 0; j < opt_flagsv.size(); j++) {
if (gen_flag_rules(prof, count, flagsv[i], opt_flagsv[j]) == RULE_ERROR)
goto fail;
vec[0] = mntbuf.c_str();
} else {
if (!convert_entry(mntbuf, device))
goto fail;
vec[0] = mntbuf.c_str();
}
/* skip device */
vec[1] = default_match_pattern;
/* skip type */
vec[2] = default_match_pattern;
tmpflags = flags;
tmpinv_flags = inv_flags;
if (tmpflags != MS_ALL_FLAGS)
tmpflags &= MS_REMOUNT_FLAGS;
if (tmpinv_flags != MS_ALL_FLAGS)
tmpflags &= MS_REMOUNT_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
goto fail;
vec[3] = flagsbuf;
if (opts)
tmpallow = AA_MATCH_CONT;
else
tmpallow = allow;
/* rule for match without required data || data MATCH_CONT */
if (!prof.policy.rules->add_rule_vec(deny, tmpallow,
audit | AA_AUDIT_MNT_DATA, 4,
vec, dfaflags, false))
goto fail;
count++;
if (opts) {
/* rule with data match required */
optsbuf.clear();
if (!build_mnt_opts(optsbuf, opts))
goto fail;
vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(deny, allow,
audit | AA_AUDIT_MNT_DATA,
5, vec, dfaflags, false))
goto fail;
count++;
}
}
if ((allow & AA_MAY_MOUNT) && (flags & MS_BIND)
&& !dev_type && !opts) {
/* bind mount rules can't be conditional on dev_type or data */
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
if (!clear_and_convert_entry(devbuf, device))
goto fail;
vec[1] = devbuf.c_str();
/* skip type */
vec[2] = default_match_pattern;
tmpflags = flags;
tmpinv_flags = inv_flags;
if (tmpflags != MS_ALL_FLAGS)
tmpflags &= MS_BIND_FLAGS;
if (tmpinv_flags != MS_ALL_FLAGS)
tmpflags &= MS_BIND_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
goto fail;
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(deny, allow, audit, 4, vec,
dfaflags, false))
goto fail;
count++;
}
if ((allow & AA_MAY_MOUNT) &&
(flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
&& !device && !dev_type && !opts) {
/* change type base rules can not be conditional on device,
* device type or data
*/
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
/* skip device and type */
vec[1] = default_match_pattern;
vec[2] = default_match_pattern;
tmpflags = flags;
tmpinv_flags = inv_flags;
if (tmpflags != MS_ALL_FLAGS)
tmpflags &= MS_MAKE_FLAGS;
if (tmpinv_flags != MS_ALL_FLAGS)
tmpflags &= MS_MAKE_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
goto fail;
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(deny, allow, audit, 4, vec,
dfaflags, false))
goto fail;
count++;
}
if ((allow & AA_MAY_MOUNT) && (flags & MS_MOVE)
&& !dev_type && !opts) {
/* mount move rules can not be conditional on dev_type,
* or data
*/
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
if (!clear_and_convert_entry(devbuf, device))
goto fail;
vec[1] = devbuf.c_str();
/* skip type */
vec[2] = default_match_pattern;
tmpflags = flags;
tmpinv_flags = inv_flags;
if (tmpflags != MS_ALL_FLAGS)
tmpflags &= MS_MOVE_FLAGS;
if (tmpinv_flags != MS_ALL_FLAGS)
tmpflags &= MS_MOVE_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
goto fail;
vec[3] = flagsbuf;
if (!prof.policy.rules->add_rule_vec(deny, allow, audit, 4, vec,
dfaflags, false))
goto fail;
count++;
}
if ((allow & AA_MAY_MOUNT) &&
(flags | inv_flags) & ~MS_CMDS) {
int tmpallow;
/* generic mount if flags are set that are not covered by
* above commands
*/
/* rule class single byte header */
mntbuf.assign(class_mount_hdr);
if (!convert_entry(mntbuf, mnt_point))
goto fail;
vec[0] = mntbuf.c_str();
if (!clear_and_convert_entry(devbuf, device))
goto fail;
vec[1] = devbuf.c_str();
typebuf.clear();
if (!build_list_val_expr(typebuf, dev_type))
goto fail;
vec[2] = typebuf.c_str();
tmpflags = flags;
tmpinv_flags = inv_flags;
if (tmpflags != MS_ALL_FLAGS)
tmpflags &= ~MS_CMDS;
if (tmpinv_flags != MS_ALL_FLAGS)
tmpinv_flags &= ~MS_CMDS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
goto fail;
vec[3] = flagsbuf;
if (opts)
tmpallow = AA_MATCH_CONT;
else
tmpallow = allow;
/* rule for match without required data || data MATCH_CONT */
if (!prof.policy.rules->add_rule_vec(deny, tmpallow,
audit | AA_AUDIT_MNT_DATA, 4,
vec, dfaflags, false))
goto fail;
count++;
if (opts) {
/* rule with data match required */
optsbuf.clear();
if (!build_mnt_opts(optsbuf, opts))
goto fail;
vec[4] = optsbuf.c_str();
if (!prof.policy.rules->add_rule_vec(deny, allow,
audit | AA_AUDIT_MNT_DATA,
5, vec, dfaflags, false))
goto fail;
count++;
}
}
if (allow & AA_MAY_UMOUNT) {

View File

@@ -20,6 +20,7 @@
#define __AA_MOUNT_H
#include <ostream>
#include <vector>
#include "parser.h"
#include "rule.h"
@@ -39,6 +40,8 @@
#define MS_MAND (1 << 6)
#define MS_NOMAND 0
#define MS_DIRSYNC (1 << 7)
#define MS_SYMFOLLOW 0
#define MS_NOSYMFOLLOW (1 << 8)
#define MS_NODIRSYNC 0
#define MS_NOATIME (1 << 10)
#define MS_ATIME 0
@@ -61,6 +64,7 @@
#define MS_IVERSION (1 << 23)
#define MS_NOIVERSION 0
#define MS_STRICTATIME (1 << 24)
#define MS_LAZYTIME (1 << 25)
#define MS_NOUSER (1 << 31)
#define MS_USER 0
@@ -74,12 +78,14 @@
#define MS_ALL_FLAGS (MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC | \
MS_SYNC | MS_REMOUNT | MS_MAND | MS_DIRSYNC | \
MS_NOSYMFOLLOW | \
MS_NOATIME | MS_NODIRATIME | MS_BIND | MS_RBIND | \
MS_MOVE | MS_VERBOSE | MS_ACL | \
MS_UNBINDABLE | MS_RUNBINDABLE | \
MS_PRIVATE | MS_RPRIVATE | \
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED | \
MS_RELATIME | MS_IVERSION | MS_STRICTATIME | MS_USER)
MS_RELATIME | MS_IVERSION | MS_STRICTATIME | \
MS_LAZYTIME | MS_USER)
/* set of flags we don't use but define (but not with the kernel values)
* for MNT_FLAGS
@@ -94,16 +100,15 @@
MS_KERNMOUNT | MS_STRICTATIME)
#define MS_BIND_FLAGS (MS_BIND | MS_RBIND)
#define MS_MAKE_FLAGS ((MS_UNBINDABLE | MS_RUNBINDABLE | \
#define MS_MAKE_CMDS (MS_UNBINDABLE | MS_RUNBINDABLE | \
MS_PRIVATE | MS_RPRIVATE | \
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED) | \
(MS_ALL_FLAGS & ~(MNT_FLAGS)))
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED)
#define MS_MAKE_FLAGS (MS_ALL_FLAGS & ~(MNT_FLAGS))
#define MS_MOVE_FLAGS (MS_MOVE)
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_RBIND | \
MS_UNBINDABLE | MS_RUNBINDABLE | MS_PRIVATE | MS_RPRIVATE | \
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED)
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_RBIND | MS_MAKE_CMDS)
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT & ~MS_BIND & ~MS_RBIND))
#define MS_NEW_FLAGS (MS_ALL_FLAGS & ~MS_CMDS)
#define MNT_SRC_OPT 1
#define MNT_DST_OPT 2
@@ -121,6 +126,19 @@
class mnt_rule: public rule_t {
int gen_policy_remount(Profile &prof, int &count, unsigned int flags,
unsigned int opt_flags);
int gen_policy_bind_mount(Profile &prof, int &count, unsigned int flags,
unsigned int opt_flags);
int gen_policy_change_mount_type(Profile &prof, int &count,
unsigned int flags,
unsigned int opt_flags);
int gen_policy_move_mount(Profile &prof, int &count, unsigned int flags,
unsigned int opt_flags);
int gen_policy_new_mount(Profile &prof, int &count, unsigned int flags,
unsigned int opt_flags);
int gen_flag_rules(Profile &prof, int &count, unsigned int flags,
unsigned int opt_flags);
public:
char *mnt_point;
char *device;
@@ -128,7 +146,7 @@ public:
struct value_list *dev_type;
struct value_list *opts;
unsigned int flags, inv_flags;
std::vector<unsigned int> flagsv, opt_flagsv;
int allow, audit;
int deny;

View File

@@ -232,6 +232,7 @@ do { \
#endif
#define list_first(LIST) (LIST)
#define list_for_each(LIST, ENTRY) \
for ((ENTRY) = (LIST); (ENTRY); (ENTRY) = (ENTRY)->next)
#define list_for_each_safe(LIST, ENTRY, TMP) \
@@ -265,6 +266,16 @@ do { \
prev; \
})
#define list_pop(LIST) \
({ \
typeof(LIST) _entry = (LIST); \
if (LIST) { \
(LIST) = (LIST)->next; \
_entry->next = NULL; \
} \
_entry; \
})
#define list_remove_at(LIST, PREV, ENTRY) \
if (PREV) \
(PREV)->next = (ENTRY)->next; \

View File

@@ -165,6 +165,7 @@ FILE *search_path(char *filename, char **fullpath, bool *skip)
if (g_includecache->find(buf)) {
/* hit do not want to re-include */
*skip = true;
free(buf);
return NULL;
}

View File

@@ -1146,7 +1146,7 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
retval = process_binary(option, kernel_interface,
cachename);
if (!retval || skip_bad_cache_rebuild)
return retval;
goto out;
}
}
@@ -1209,7 +1209,8 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
}
}
out:
/* cleanup */
reset_parser(profilename);
return retval;
}
@@ -1696,6 +1697,7 @@ int main(int argc, char *argv[])
if (ofile)
fclose(ofile);
aa_policy_cache_unref(policy_cache);
aa_kernel_interface_unref(kernel_interface);
return last_error;
}

View File

@@ -1773,7 +1773,7 @@ static int abi_features_base(struct aa_features **features, char *filename, bool
{
autofclose FILE *f = NULL;
struct stat my_stat;
char *fullpath = NULL;
autofree char *fullpath = NULL;
bool cached;
if (search) {

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule with incompatible options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(rw, ro) -> /foo,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule with incompatible options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(rw ro) -> /foo,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule with incompatible options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(rw ro) fstype=procfs -> /foo,
}

View File

@@ -1,6 +1,6 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#=Description basic mount rule with incompatible options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(rw ro) fstype=(procfs) none -> /foo,

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule conflicting = options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(strictatime, nostrictatime) -> /foo,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule conflicting = options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(lazytime, nolazytime) -> /foo,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule conflicting = options
#=EXRESULT FAIL
#
/usr/bin/foo {
mount options=(symfollow, nosymfollow) -> /foo,
}

View File

@@ -3,5 +3,5 @@
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw, ro) -> /foo,
mount options=(rw nosuid) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw ro) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw ro) fstype=procfs -> /foo,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic rules to test the "nostrictatime" mount option
#=EXRESULT PASS
/usr/bin/foo {
mount options=nostrictatime /a -> /1,
mount options=(nostrictatime) /b -> /2,
mount options in (nostrictatime) /d -> /4,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic rules to test the "lazytime" mount option
#=EXRESULT PASS
/usr/bin/foo {
mount options=lazytime /a -> /1,
mount options=(lazytime) /b -> /2,
mount options in (lazytime) /d -> /4,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic rules to test the "nolazytime" mount option
#=EXRESULT PASS
/usr/bin/foo {
mount options=nolazytime /a -> /1,
mount options=(nolazytime) /b -> /2,
mount options in (nolazytime) /d -> /4,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic rules to test the "strictatime" mount option in combination
#=EXRESULT PASS
/usr/bin/foo {
mount options=(rw,strictatime) /c -> /3,
mount options in (ro,strictatime) /e -> /5,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic rules to test the "nostrictatime" mount option in combination
#=EXRESULT PASS
/usr/bin/foo {
mount options=(rw,nostrictatime) /c -> /3,
mount options in (ro,nostrictatime) /e -> /5,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic rules to test the "lazytime" mount option in combination
#=EXRESULT PASS
/usr/bin/foo {
mount options=(rw,lazytime) /c -> /3,
mount options in (ro,lazytime) /e -> /5,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic rules to test the "nolazytime" mount option in combination
#=EXRESULT PASS
/usr/bin/foo {
mount options=(rw,nolazytime) /c -> /3,
mount options in (ro,nolazytime) /e -> /5,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule conflicting options with in
#=EXRESULT PASS
#
/usr/bin/foo {
mount options in (strictatime, nostrictatime) -> /foo,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule conflicting options with in
#=EXRESULT PASS
#
/usr/bin/foo {
mount options in (lazytime, nolazytime) -> /foo,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic rules to test the "nosymfollow" mount option
#=EXRESULT PASS
/usr/bin/foo {
mount options=nosymfollow /a -> /1,
mount options=(nosymfollow) /b -> /2,
mount options in (nosymfollow) /d -> /4,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic rules to test the "symfollow" mount option in combination
#=EXRESULT PASS
/usr/bin/foo {
mount options=(rw,symfollow) /c -> /3,
mount options in (ro,symfollow) /e -> /5,
}

View File

@@ -0,0 +1,7 @@
#
#=Description basic mount rule conflicting options with in
#=EXRESULT PASS
#
/usr/bin/foo {
mount options in (symfollow, nosymfollow) -> /foo,
}

View File

@@ -83,7 +83,7 @@ local:
fn=$$(basename $$profile); \
echo "# Site-specific additions and overrides for '$$fn'" > ${PROFILES_SOURCE}/local/$$fn; \
grep "include[[:space:]]\\+if[[:space:]]\\+exists[[:space:]]\\+<local/$$fn>" "$$profile" >/dev/null || { echo "$$profile doesn't contain include if exists <local/$$fn>" ; exit 1; } ; \
done; \
done
.PHONY: install
install: local
@@ -119,7 +119,7 @@ CHECK_PROFILES=$(filter-out ${IGNORE_FILES} ${SUBDIRS}, $(wildcard ${PROFILES_SO
CHECK_ABSTRACTIONS=$(shell find ${ABSTRACTIONS_SOURCE} -type f -print)
.PHONY: check
check: check-parser check-logprof check-abstractions.d
check: check-parser check-logprof check-abstractions.d check-extras
.PHONY: check-parser
check-parser: test-dependencies local
@@ -151,3 +151,11 @@ check-abstractions.d:
test "$$file" = 'ubuntu-helpers' && continue ; \
grep -q "^ include if exists <abstractions/$${file}.d>$$" $$file || { echo "$$file does not contain 'include if exists <abstractions/$${file}.d>'"; exit 1; } ; \
done
.PHONY: check-extras
check-extras:
@echo "*** Checking if all extra profiles contain include if exists <local/*>"
$(Q)cd ${EXTRAS_SOURCE} && for file in * ; do \
test "$$file" = 'README' && continue ; \
grep -q "^ include if exists <local/$${file}>$$" $$file || { echo "$$file does not contain 'include if exists <local/$${file}>'"; exit 1; } ; \
done

View File

@@ -36,8 +36,8 @@
/usr/share/locale-langpack/** r,
/usr/share/locale/** r,
/usr/share/**/locale/** r,
/usr/share/zoneinfo/ r,
/usr/share/zoneinfo/** r,
/usr/share/zoneinfo{,-icu}/ r,
/usr/share/zoneinfo{,-icu}/** r,
/usr/share/X11/locale/** r,
@{run}/systemd/journal/dev-log w,
# systemd native journal API (see sd_journal_print(4))

View File

@@ -20,7 +20,7 @@
@{system_share_dirs}/mime/** r,
# per-user configurations
owner @{HOME}/.icons/ r,
owner @{HOME}/.icons/{,**} r,
owner @{HOME}/.recently-used.xbel* rw,
owner @{HOME}/.local/share/recently-used.xbel* rw,
owner @{HOME}/.config/user-dirs.dirs r,

View File

@@ -22,6 +22,11 @@
/usr/lib/@{multiarch}/krb5/plugins/preauth/ r,
/usr/lib/@{multiarch}/krb5/plugins/preauth/* mr,
/usr/lib{,32,64}/krb5/plugins/authdata/ r,
/usr/lib{,32,64}/krb5/plugins/authdata/* mr,
/usr/lib/@{multiarch}/krb5/plugins/authdata/ r,
/usr/lib/@{multiarch}/krb5/plugins/authdata/* mr,
/etc/krb5.keytab rk,
/etc/krb5.conf r,
/etc/krb5.conf.d/ r,

View File

@@ -23,7 +23,7 @@
/var/lib/samba/** rwk,
/var/log/samba/cores/ rw,
/var/log/samba/cores/** rw,
/var/log/samba/* w,
/var/log/samba/* rw,
@{run}/{,lock/}samba/ w,
@{run}/{,lock/}samba/*.tdb rwk,
@{run}/{,lock/}samba/msg.{lock,sock}/ rwk,

View File

@@ -30,6 +30,7 @@ profile syslogd /{usr/,}{bin,sbin}/syslogd {
/dev/log wl,
/var/lib/*/dev/log wl,
/dev/kmsg r,
/proc/kmsg r,
/dev/tty* w,

View File

@@ -13,11 +13,15 @@
# with the goal of having only user-modified config files in /etc/, directories
# like /usr/etc/ get introduced for storing the default config.
# @{etc_ro} contains read-only directories with configuration files.
# @{etc_ro} contains directories with configuration files, including read-only directories.
# Do not use @{etc_ro} in rules that allow write access.
@{etc_ro}=/etc/ /usr/etc/
# @{etc_rw} contains directories where writing to configuration files is allowed.
# @{etc_rw} should always be a subset of @{etc_ro}.
#
# Only use @{etc_rw} if the profile allows writing to a configuration file.
# For rules that only allows read access, use @{etc_ro}.
@{etc_rw}=/etc/
# Also, include files in tunables/etc.d/ for site-specific adjustments to

View File

@@ -45,6 +45,9 @@ profile nscd /usr/{bin,sbin}/nscd {
/{etc,run,run/host,/usr/lib}/userdb/ r,
/{etc,run,run/host,/usr/lib}/userdb/*.{user,user-privileged,group,group-privileged} r,
# needed by unscd
@{run}/systemd/notify w,
# Site-specific additions and overrides. See local/README for details.
include if exists <local/usr.sbin.nscd>
}

View File

@@ -6,6 +6,7 @@ profile winbindd /usr/{bin,sbin}/winbindd {
include <abstractions/base>
include <abstractions/nameservice>
include <abstractions/samba>
include <abstractions/kerberosclient>
deny capability block_suspend,
@@ -30,6 +31,7 @@ profile winbindd /usr/{bin,sbin}/winbindd {
/usr/{bin,sbin}/winbindd mr,
/var/cache/krb5rcache/* rwk,
/var/cache/samba/*.tdb rwk,
/var/lib/sss/pubconf/kdcinfo.* r,
/var/log/samba/log.winbindd rw,
@{run}/{samba/,}winbindd.pid rwk,
@{run}/samba/winbindd/ rw,

View File

@@ -18,7 +18,7 @@ include <tunables/global>
# /usr/lib/firefox-4.0b8/firefox
# but not:
# /usr/lib/firefox-4.0b8/firefox.sh
/usr/lib/firefox{,-[0-9]*}/firefox{,*[^s][^h]} {
profile firefox /usr/lib/firefox{,-[0-9]*}/firefox{,*[^s][^h]} {
include <abstractions/audio>
include <abstractions/cups-client>
include <abstractions/dbus-session>
@@ -128,7 +128,6 @@ include <tunables/global>
@{HOME}/.mozilla/firefox/*/gmp-widevinecdm/*/lib*so m,
# Site-specific additions and overrides. See local/README for details.
# Local path is disabled, we only enable them for profiles we promote
# out of extras.
include if exists <local/usr.bin.firefox>
include if exists <local/firefox>
}

View File

@@ -4,7 +4,7 @@ abi <abi/3.0>,
include <tunables/global>
/usr/lib/firefox/firefox.sh {
profile firefox.sh /usr/lib/firefox/firefox.sh {
include <abstractions/base>
include <abstractions/bash>
include <abstractions/consoles>

View File

@@ -328,6 +328,9 @@ unix_fd_client: unix_fd_client.c unix_fd_common.o
attach_disconnected: attach_disconnected.c unix_fd_common.o
${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ ${LDLIBS}
mount: mount.c
${CC} ${CFLAGS} -std=gnu99 ${LDFLAGS} $^ -o $@ ${LDLIBS}
build-dep:
@if [ `whoami` = "root" ] ;\
then \

View File

@@ -16,7 +16,7 @@ fi
out=$($1 -- cat /proc/self/attr/current 2>&1)
rc=$?
if [ $rc -eq 0 ] && [ "$out" == "$2" ]; then
if [ $rc -eq 0 ] && [ "$out" = "$2" ]; then
echo PASS
exit 0
elif [ $rc -ne 0 ]; then

View File

@@ -55,7 +55,7 @@ fi
# MS_PRIVATE temporarily.
FINDMNT=/bin/findmnt
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no PROPAGATION / > /dev/null 2>&1 ; then
if [ "$(${FINDMNT} -no PROPAGATION /)" == "shared" ] ; then
if [ "$(${FINDMNT} -no PROPAGATION /)" = "shared" ] ; then
root_was_shared="yes"
fi
elif [ "$(ps hp1 -ocomm)" = "systemd" ] ; then

View File

@@ -97,7 +97,7 @@ for TEST in ${TESTS} ; do
# no capabilities allowed
genprofile ${my_entries}
if [ "${TEST}" == "syscall_ptrace" -a "$(kernel_features ptrace)" == "true" ] ; then
if [ "${TEST}" = "syscall_ptrace" -a "$(kernel_features ptrace)" = "true" ] ; then
# ptrace between profiles confining tasks of same pid is controlled by the ptrace rule
# capability + ptrace rule needed between pids
runchecktest "${TEST} -- no caps" pass ${my_arg}
@@ -111,9 +111,9 @@ for TEST in ${TESTS} ; do
# iterate through each of the capabilities
for cap in ${CAPABILITIES} ; do
if [ "X$(eval echo \${${TEST}_${cap}})" == "XTRUE" ] ; then
if [ "X$(eval echo \${${TEST}_${cap}})" = "XTRUE" ] ; then
expected_result=pass
elif [ "${TEST}" == "syscall_ptrace" -a "$(kernel_features ptrace)" == "true" ]; then
elif [ "${TEST}" = "syscall_ptrace" -a "$(kernel_features ptrace)" = "true" ]; then
expected_result=pass
else
expected_result=fail
@@ -126,7 +126,7 @@ for TEST in ${TESTS} ; do
# a subprofile.
settest ${testwrapper}
genprofile hat:$bin/${TEST} addimage:${bin}/${TEST} ${my_entries}
if [ "${TEST}" == "syscall_ptrace" -a "$(kernel_features ptrace)" == "true" ] ; then
if [ "${TEST}" = "syscall_ptrace" -a "$(kernel_features ptrace)" = "true" ] ; then
# ptrace between profiles confining tasks of same pid is controlled by the ptrace rule
# capability + ptrace rule needed between pids
runchecktest "${TEST} changehat -- no caps" pass $bin/${TEST} ${my_arg}
@@ -139,9 +139,9 @@ for TEST in ${TESTS} ; do
runchecktest "${TEST} changehat -- all caps" pass $bin/${TEST} ${my_arg}
for cap in ${CAPABILITIES} ; do
if [ "X$(eval echo \${${TEST}_${cap}})" == "XTRUE" ] ; then
if [ "X$(eval echo \${${TEST}_${cap}})" = "XTRUE" ] ; then
expected_result=pass
elif [ "${TEST}" == "syscall_ptrace" -a "$(kernel_features ptrace)" == "true" ]; then
elif [ "${TEST}" = "syscall_ptrace" -a "$(kernel_features ptrace)" = "true" ]; then
expected_result=pass
else
expected_result=fail
@@ -156,75 +156,75 @@ cap=sys_chroot
settest syscall_chroot
# test deny keyword works
genprofile cap:${cap}:deny ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, deny keyword" fail ${syscall_chroot_args}
# test allow keyword works
genprofile cap:${cap}:allow ${syscall_chroot_extra_entries}
genprofile qual=allow:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow keyword" pass ${syscall_chroot_args}
### allow/deny overlap tests ###
# test allow & deny keyword behavior, allow first
genprofile cap:${cap}:allow cap:${cap}:deny ${syscall_chroot_extra_entries}
genprofile qual=allow:cap:${cap} qual=deny:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow & deny keyword, allow first" fail ${syscall_chroot_args}
# test implicit allow & deny keyword behavior, allow first
genprofile cap:${cap} cap:${cap}:deny ${syscall_chroot_extra_entries}
genprofile cap:${cap} qual=deny:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow & deny keyword, allow first" fail ${syscall_chroot_args}
# test allow & deny keyword behavior, deny first
genprofile cap:${cap}:deny cap:${cap}:allow ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:${cap} qual=allow:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow & deny keyword, deny first" fail ${syscall_chroot_args}
# test implicit allow & deny keyword behavior, deny first
genprofile cap:${cap}:deny cap:${cap} ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:${cap} cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow & deny keyword, deny first" fail ${syscall_chroot_args}
# test allow all & deny all capability keyword behavior, allow first
genprofile cap:ALL:allow cap:ALL:deny ${syscall_chroot_extra_entries}
genprofile qual=allow:cap:ALL qual=deny:cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow & deny all caps keyword, allow first" fail ${syscall_chroot_args}
# test implicit allow all & deny all capability keyword behavior, allow first
genprofile cap:ALL cap:ALL:deny ${syscall_chroot_extra_entries}
genprofile cap:ALL qual=deny:cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow all & deny all caps keyword, allow first" fail ${syscall_chroot_args}
# test allow all & deny all capability keyword behavior, deny first
genprofile cap:ALL:deny cap:ALL:allow ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:ALL qual=allow:cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow & deny all caps keyword, deny first" fail ${syscall_chroot_args}
# test implicit allow all & deny all capability keyword behavior, deny first
genprofile cap:ALL:deny cap:ALL ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:ALL cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow & deny all caps keyword, deny first" fail ${syscall_chroot_args}
# test allow all & deny keywords behavior, allow first
genprofile cap:ALL:allow cap:${cap}:deny ${syscall_chroot_extra_entries}
genprofile qual=allow:cap:ALL qual=deny:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow all & deny keyword, allow first" fail ${syscall_chroot_args}
# test implicit allow all & deny keywords behavior, allow first
genprofile cap:ALL cap:${cap}:deny ${syscall_chroot_extra_entries}
genprofile cap:ALL qual=deny:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow all & deny keyword, allow first" fail ${syscall_chroot_args}
# test allow all & deny keywords behavior, deny first
genprofile cap:${cap}:deny cap:ALL:allow ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:${cap} qual=allow:cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow all & deny keyword, deny first" fail ${syscall_chroot_args}
# test implicit allow all & deny keywords behavior, deny first
genprofile cap:${cap}:deny cap:ALL ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:${cap} cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow all & deny keyword, deny first" fail ${syscall_chroot_args}
# test allow & deny all keywords behavior, allow first
genprofile cap:${cap}:allow cap:ALL:deny ${syscall_chroot_extra_entries}
genprofile qual=allow:cap:${cap} qual=deny:cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow & deny all keyword, allow first" fail ${syscall_chroot_args}
# test implicit allow & deny all keywords behavior, allow first
genprofile cap:${cap} cap:ALL:deny ${syscall_chroot_extra_entries}
genprofile cap:${cap} qual=deny:cap:ALL ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow & deny all keyword, allow first" fail ${syscall_chroot_args}
# test allow & deny all keywords behavior, deny first
genprofile cap:ALL:deny cap:${cap}:allow ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:ALL qual=allow:cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, allow & deny all keyword, deny first" fail ${syscall_chroot_args}
# test implicit allow & deny all keywords behavior, deny first
genprofile cap:ALL:deny cap:${cap} ${syscall_chroot_extra_entries}
genprofile qual=deny:cap:ALL cap:${cap} ${syscall_chroot_extra_entries}
runchecktest "syscall_chroot -- capability ${cap}, implicit allow & deny all keyword, deny first" fail ${syscall_chroot_args}

View File

@@ -47,7 +47,7 @@ runchecktest "NO CHANGEPROFILE (access parent file)" pass nochange $file
runchecktest "NO CHANGEPROFILE (access sub file)" fail nochange $subfile
errno=EACCES
if [ "$(kernel_features domain/stack)" == "true" ]; then
if [ "$(kernel_features domain/stack)" = "true" ]; then
# The returned errno changed in the set of kernel patches that
# introduced AppArmor profile stacking
errno=ENOENT

View File

@@ -18,7 +18,7 @@ cleancorefile()
checkcorefile()
{
# global _testdesc _pfmode _known outfile
if [ ${1:0:1} == "x" ] ; then
if [ ${1:0:1} = "x" ] ; then
requirement=${1#x}
_known=" (known problem)"
else

View File

@@ -128,7 +128,8 @@ start_dbus_daemon()
return 1
fi
out=$(dbus-daemon --fork --print-pid --print-address --config-file=dbus.conf)
bus_addr=$(mktemp --dry-run /tmp/dbus-XXXXXX)
out=$(dbus-daemon --fork --print-pid --print-address --address="unix:abstract=$bus_addr" --config-file=dbus.conf)
if [ $? -ne 0 ]
then
echo "Failed to start DBus daemon"

View File

@@ -65,7 +65,9 @@ okperm=rwl
badperm=wl
af_unix=""
if [ "$(kernel_features network/af_unix)" == "true" -a "$(parser_supports 'unix,')" == "true" ]; then
if [ "$(kernel_features network_v8)" = "true" -a "$(parser_supports 'unix,')" = "true" ]; then
af_unix="unix:create"
elif [ "$(kernel_features network/af_unix)" = "true" -a "$(parser_supports 'unix,')" = "true" ]; then
af_unix="unix:create"
fi

View File

@@ -57,7 +57,7 @@ local_runchecktest()
checktestbg
if [ "$teststatus" == "pass" -a -n "$actual_confinement" -a "$actual_confinement" != "$expected_confinement" ]
if [ "$teststatus" = "pass" -a -n "$actual_confinement" -a "$actual_confinement" != "$expected_confinement" ]
then
echo "Error: ${testname} failed. Test '${_testdesc}' actual confinement '$actual_confinement' differed from expected confinement '$expected_confinement'"
testfailed

View File

@@ -51,7 +51,7 @@ touch $file $otherfile $sharedfile $thirdfile
# meaning the below conditional check has the wrong results for those
# kernels. Since this test is not about testing mmap just always add
# the mmap perm
#if [ "$(kernel_features domain/fix_binfmt_elf_mmap)" == "true" ]; then
#if [ "$(kernel_features domain/fix_binfmt_elf_mmap)" = "true" ]; then
# elfmmap="m"
#else
# elfmmap=""

View File

@@ -37,10 +37,17 @@ sub usage {
print STDERR "Usage $0 [--nowarn|--escape] execname [rules]\n";
print STDERR " $0 --help\n";
print STDERR " $0 --stdin\n";
print STDERR "Options:\n";
print STDERR " nowarn: don't warn if execname does not exist\n";
print STDERR " nodefault: don't include default rules/ldd output\n";
print STDERR " escape: escape stuff that would be treated as regexs\n";
print STDERR " help: print this message\n";
print STDERR "Rule Qualifiers:\n";
print STDERR " qualifiers can optionally be added to a rule with 'qual='\n";
print STDERR " Examples:\n";
print STDERR " /path/to/file:rw\n";
print STDERR " qual=audit:/path/to/file:rw\n";
print STDERR " qual=audit,deny:/path/to/file:rw\n";
}
# genprofile passes in $bin:w as default rule atm
@@ -146,185 +153,183 @@ sub gen_binary($) {
}
}
sub gen_netdomain($) {
my $rule = shift;
sub gen_netdomain($@) {
my ($rule, $qualifier) = @_;
# only split on single ':'s
my @rules = split (/(?<!:):(?!:)/, $rule);
# convert '::' to ':' -- for port designations
foreach (@rules) { s/::/:/g; }
push (@{$output_rules{$hat}}, " @rules,\n");
push (@{$output_rules{$hat}}, " ${qualifier}@rules,\n");
}
sub gen_network($) {
my $rule = shift;
sub gen_network($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
push (@{$output_rules{$hat}}, " @rules,\n");
push (@{$output_rules{$hat}}, " ${qualifier}@rules,\n");
}
sub gen_unix($) {
my $rule = shift;
sub gen_unix($@) {
my ($rule, $qualifier) = @_;
if ($rule =~ /^unix:ALL$/) {
push (@{$output_rules{$hat}}, " unix,\n");
} else {
$rule =~ s/:/ /g;
push(@{$output_rules{$hat}}, " " . $rule . ",\n");
push(@{$output_rules{$hat}}, " " . $qualifier . $rule . ",\n");
}
}
sub gen_cap($) {
my $rule = shift;
sub gen_cap($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " capability,\n");
push (@{$output_rules{$hat}}, " ${qualifier}capability,\n");
} else {
push (@{$output_rules{$hat}}, " capability $rules[1],\n");
}
} elsif (@rules == 3) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " $rules[2] capability,\n");
} else {
push (@{$output_rules{$hat}}, " $rules[2] capability $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}capability $rules[1],\n");
}
} else {
(!$nowarn) && print STDERR "Warning: invalid capability description '$rule', ignored\n";
}
}
sub gen_ptrace($) {
my $rule = shift;
sub gen_ptrace($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " ptrace,\n");
push (@{$output_rules{$hat}}, " ${qualifier}ptrace,\n");
} else {
push (@{$output_rules{$hat}}, " ptrace $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}ptrace $rules[1],\n");
}
} elsif (@rules == 3) {
push (@{$output_rules{$hat}}, " ptrace $rules[1] $rules[2],\n");
push (@{$output_rules{$hat}}, " ${qualifier}ptrace $rules[1] $rules[2],\n");
} else {
(!$nowarn) && print STDERR "Warning: invalid ptrace description '$rule', ignored\n";
}
}
sub gen_signal($) {
my $rule = shift;
sub gen_signal($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " signal,\n");
push (@{$output_rules{$hat}}, " ${qualifier}signal,\n");
} else {
push (@{$output_rules{$hat}}, " signal $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}signal $rules[1],\n");
}
} elsif (@rules == 3) {
push (@{$output_rules{$hat}}, " signal $rules[1] $rules[2],\n");
push (@{$output_rules{$hat}}, " ${qualifier}signal $rules[1] $rules[2],\n");
} else {
(!$nowarn) && print STDERR "Warning: invalid signal description '$rule', ignored\n";
}
}
sub gen_mount($) {
my $rule = shift;
sub gen_mount($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " mount,\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount,\n");
} else {
push (@{$output_rules{$hat}}, " mount $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount $rules[1],\n");
}
} elsif (@rules == 3) {
push (@{$output_rules{$hat}}, " mount $rules[1] $rules[2],\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount $rules[1] $rules[2],\n");
} elsif (@rules == 4) {
push (@{$output_rules{$hat}}, " mount $rules[1] $rules[2] $rules[3],\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount $rules[1] $rules[2] $rules[3],\n");
} elsif (@rules == 5) {
push (@{$output_rules{$hat}}, " mount $rules[1] $rules[2] $rules[3] $rules[4],\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount $rules[1] $rules[2] $rules[3] $rules[4],\n");
} elsif (@rules == 6) {
push (@{$output_rules{$hat}}, " mount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5],\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5],\n");
} elsif (@rules == 7) {
push (@{$output_rules{$hat}}, " mount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5] $rules[6],\n");
push (@{$output_rules{$hat}}, " ${qualifier}mount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5] $rules[6],\n");
} else {
(!$nowarn) && print STDERR "Warning: invalid mount description '$rule', ignored\n";
}
}
sub gen_remount($) {
my $rule = shift;
sub gen_remount($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " remount,\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount,\n");
} else {
push (@{$output_rules{$hat}}, " remount $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount $rules[1],\n");
}
} elsif (@rules == 3) {
push (@{$output_rules{$hat}}, " remount $rules[1] $rules[2],\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount $rules[1] $rules[2],\n");
} elsif (@rules == 4) {
push (@{$output_rules{$hat}}, " remount $rules[1] $rules[2] $rules[3],\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount $rules[1] $rules[2] $rules[3],\n");
} elsif (@rules == 5) {
push (@{$output_rules{$hat}}, " remount $rules[1] $rules[2] $rules[3] $rules[4],\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount $rules[1] $rules[2] $rules[3] $rules[4],\n");
} elsif (@rules == 6) {
push (@{$output_rules{$hat}}, " remount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5],\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5],\n");
} elsif (@rules == 7) {
push (@{$output_rules{$hat}}, " remount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5] $rules[6],\n");
push (@{$output_rules{$hat}}, " ${qualifier}remount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5] $rules[6],\n");
} else {
(!$nowarn) && print STDERR "Warning: invalid remount description '$rule', ignored\n";
}
}
sub gen_umount($) {
my $rule = shift;
sub gen_umount($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " umount,\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount,\n");
} else {
push (@{$output_rules{$hat}}, " umount $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount $rules[1],\n");
}
} elsif (@rules == 3) {
push (@{$output_rules{$hat}}, " umount $rules[1] $rules[2],\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount $rules[1] $rules[2],\n");
} elsif (@rules == 4) {
push (@{$output_rules{$hat}}, " umount $rules[1] $rules[2] $rules[3],\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount $rules[1] $rules[2] $rules[3],\n");
} elsif (@rules == 5) {
push (@{$output_rules{$hat}}, " umount $rules[1] $rules[2] $rules[3] $rules[4],\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount $rules[1] $rules[2] $rules[3] $rules[4],\n");
} elsif (@rules == 6) {
push (@{$output_rules{$hat}}, " umount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5],\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5],\n");
} elsif (@rules == 7) {
push (@{$output_rules{$hat}}, " umount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5] $rules[6],\n");
push (@{$output_rules{$hat}}, " ${qualifier}umount $rules[1] $rules[2] $rules[3] $rules[4] $rules[5] $rules[6],\n");
} else {
(!$nowarn) && print STDERR "Warning: invalid umount description '$rule', ignored\n";
}
}
sub gen_pivot_root($) {
my $rule = shift;
sub gen_pivot_root($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " pivot_root,\n");
push (@{$output_rules{$hat}}, " ${qualifier}pivot_root,\n");
} else {
push (@{$output_rules{$hat}}, " pivot_root $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}pivot_root $rules[1],\n");
}
} else {
(!$nowarn) && print STDERR "Warning: invalid pivot_root description '$rule', ignored\n";
}
}
sub gen_file($) {
my $rule = shift;
sub gen_file($@) {
my ($rule, $qualifier) = @_;
if (!$qualifier) {
$qualifier = "";
}
my @rules = split (/:/, $rule);
# default: file rules
if (@rules == 1) {
# support raw rules
push (@{$output_rules{$hat}}, " $rules[0],\n");
push (@{$output_rules{$hat}}, " ${qualifier}$rules[0],\n");
} elsif (@rules == 2) {
if ($escape) {
$rules[0]=~ s/(["[\]{}\:])/\\$1/g;
$rules[0]=~ s/(\#)/\\043/g;
}
if ($rules[0]=~ /[\s\!\"\^]/) {
push (@{$output_rules{$hat}}, " \"$rules[0]\" $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}\"$rules[0]\" $rules[1],\n");
} else {
push (@{$output_rules{$hat}}, " $rules[0] $rules[1],\n");
push (@{$output_rules{$hat}}, " ${qualifier}$rules[0] $rules[1],\n");
}
} else {
(!$nowarn) && print STDERR "Warning: invalid file access '$rule', ignored\n";
@@ -341,34 +346,34 @@ sub gen_flag($) {
}
}
sub gen_change_profile($) {
my $rule = shift;
sub gen_change_profile($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules == 2) {
if ($rules[1] =~ /^ALL$/) {
push (@{$output_rules{$hat}}, " change_profile,\n",);
push (@{$output_rules{$hat}}, " ${qualifier}change_profile,\n",);
} else {
push (@{$output_rules{$hat}}, " change_profile -> $rules[1],\n",);
push (@{$output_rules{$hat}}, " ${qualifier}change_profile -> $rules[1],\n",);
}
} elsif (@rules == 3) {
push (@{$output_rules{$hat}}, " change_profile $rules[1] -> $rules[2],\n",);
push (@{$output_rules{$hat}}, " ${qualifier}change_profile $rules[1] -> $rules[2],\n",);
} elsif (@rules == 4) {
push (@{$output_rules{$hat}}, " change_profile $rules[1] $rules[2] -> $rules[3],\n",);
push (@{$output_rules{$hat}}, " ${qualifier}change_profile $rules[1] $rules[2] -> $rules[3],\n",);
} else {
(!$nowarn) && print STDERR "Warning: invalid change_profile description '$rule', ignored\n";
}
}
sub gen_hat($) {
my $rule = shift;
sub gen_hat($@) {
my ($rule, $qualifier) = @_;
my @rules = split (/:/, $rule);
if (@rules != 2) {
(!$nowarn) && print STDERR "Warning: invalid hat description '$rule', ignored\n";
} else {
$hat = $rules[1];
# give every profile/hat access to change_hat
@{$output_rules{$hat}} = ( " /proc/*/attr/current w,\n",);
push(@{$output_rules{$hat}}, " /proc/*/attr/apparmor/current w,\n");
@{$output_rules{$hat}} = ( " ${qualifier}/proc/*/attr/current w,\n",);
push(@{$output_rules{$hat}}, " ${qualifier}/proc/*/attr/apparmor/current w,\n");
}
}
@@ -428,34 +433,42 @@ sub gen_from_args() {
}
for my $rule (@ARGV) {
my $qualifier = "";
if ($rule =~ /^qual=([^:]*):(.*)/) {
# Strip qualifiers from rule to pass as separate argument
$qualifier = "$1 ";
$rule = $2;
$qualifier =~ s/,/ /g;
}
#($fn, @rules) = split (/:/, $rule);
if ($rule =~ /^(tcp|udp)/) {
# netdomain rules
gen_netdomain($rule);
gen_netdomain($rule, $qualifier);
} elsif ($rule =~ /^network:/) {
gen_network($rule);
gen_network($rule, $qualifier);
} elsif ($rule =~ /^unix:/) {
gen_unix($rule);
gen_unix($rule, $qualifier);
} elsif ($rule =~ /^cap:/) {
gen_cap($rule);
gen_cap($rule, $qualifier);
} elsif ($rule =~ /^ptrace:/) {
gen_ptrace($rule);
gen_ptrace($rule, $qualifier);
} elsif ($rule =~ /^signal:/) {
gen_signal($rule);
gen_signal($rule, $qualifier);
} elsif ($rule =~ /^mount:/) {
gen_mount($rule);
gen_mount($rule, $qualifier);
} elsif ($rule =~ /^remount:/) {
gen_remount($rule);
gen_remount($rule, $qualifier);
} elsif ($rule =~ /^umount:/) {
gen_umount($rule);
gen_umount($rule, $qualifier);
} elsif ($rule =~ /^pivot_root:/) {
gen_pivot_root($rule);
gen_pivot_root($rule, $qualifier);
} elsif ($rule =~ /^flag:/) {
gen_flag($rule);
} elsif ($rule =~ /^hat:/) {
gen_hat($rule);
gen_hat($rule, $qualifier);
} elsif ($rule =~ /^change_profile:/) {
gen_change_profile($rule);
gen_change_profile($rule, $qualifier);
} elsif ($rule =~ /^addimage:/) {
gen_addimage($rule);
$addimage = 1;
@@ -464,7 +477,7 @@ sub gen_from_args() {
} elsif ($rule =~ /^path:/) {
gen_path($rule);
} else {
gen_file($rule);
gen_file($rule, $qualifier);
}
}

View File

@@ -14,27 +14,163 @@
#include <sys/stat.h>
#include <sys/mount.h>
#include <string.h>
#include <stdlib.h>
struct mnt_keyword_table {
const char *keyword;
unsigned long set;
unsigned long clear;
};
static struct mnt_keyword_table mnt_opts_table[] = {
{ "rw", 0, MS_RDONLY }, /* read-write */
{ "ro", MS_RDONLY, 0 }, /* read-only */
{ "exec", 0, MS_NOEXEC }, /* permit execution of binaries */
{ "noexec", MS_NOEXEC, 0 }, /* don't execute binaries */
{ "suid", 0, MS_NOSUID }, /* honor suid executables */
{ "nosuid", MS_NOSUID, 0 }, /* don't honor suid executables */
{ "dev", 0, MS_NODEV }, /* interpret device files */
{ "nodev", MS_NODEV, 0 }, /* don't interpret devices */
{ "async", 0, MS_SYNCHRONOUS }, /* asynchronous I/O */
{ "sync", MS_SYNCHRONOUS, 0 }, /* synchronous I/O */
{ "loud", 0, MS_SILENT }, /* print out messages. */
{ "silent", MS_SILENT, 0 }, /* be quiet */
{ "nomand", 0, MS_MANDLOCK }, /* forbid mandatory locks on this FS */
{ "mand", MS_MANDLOCK, 0 }, /* allow mandatory locks on this FS */
{ "atime", 0, MS_NOATIME }, /* update access time */
{ "noatime", MS_NOATIME, 0 }, /* do not update access time */
{ "noiversion", 0, MS_I_VERSION }, /* don't update inode I_version time */
{ "iversion", MS_I_VERSION, 0 }, /* update inode I_version time */
{ "diratime", 0, MS_NODIRATIME }, /* update dir access times */
{ "nodiratime", MS_NODIRATIME, 0 }, /* do not update dir access times */
{ "nostrictatime", 0, MS_STRICTATIME }, /* kernel default atime */
{ "strictatime", MS_STRICTATIME, 0 }, /* strict atime semantics */
/* MS_LAZYTIME added in 4.0 kernel */
#ifdef MS_LAZYTIME
{ "nolazytime", 0, MS_LAZYTIME },
{ "lazytime", MS_LAZYTIME, 0 }, /* update {a,m,c}time on the in-memory inode only */
#endif
{ "acl", MS_POSIXACL, 0 },
{ "noacl", 0, MS_POSIXACL },
{ "norelatime", 0, MS_RELATIME },
{ "relatime", MS_RELATIME, 0 },
{ "dirsync", MS_DIRSYNC, 0 }, /* synchronous directory modifications */
{ "nodirsync", 0, MS_DIRSYNC },
/* MS_NOSYMFOLLOW added in 5.10 kernel */
#ifdef MS_NOSYMFOLLOW
{ "nosymfollow", MS_NOSYMFOLLOW, 0 },
{ "symfollow", 0, MS_NOSYMFOLLOW },
#endif
{ "bind", MS_BIND, 0 }, /* remount part of the tree elsewhere */
{ "rbind", MS_BIND | MS_REC, 0 }, /* idem, plus mounted subtrees */
{ "unbindable", MS_UNBINDABLE, 0 }, /* unbindable */
{ "runbindable", MS_UNBINDABLE | MS_REC, 0 },
{ "private", MS_PRIVATE, 0 }, /* private */
{ "rprivate", MS_PRIVATE | MS_REC, 0 },
{ "slave", MS_SLAVE, 0 }, /* slave */
{ "rslave", MS_SLAVE | MS_REC, 0 },
{ "shared", MS_SHARED, 0 }, /* shared */
{ "rshared", MS_SHARED | MS_REC, 0 },
{ "move", MS_MOVE, 0 },
{ "remount", MS_REMOUNT, 0 },
};
const unsigned int mnt_opts_table_size =
sizeof(mnt_opts_table) / sizeof(struct mnt_keyword_table);
unsigned long get_mnt_opt_bit(char *key)
{
for (unsigned int i = 0; i < mnt_opts_table_size; i++) {
if (strcmp(mnt_opts_table[i].keyword, key) == 0) {
return mnt_opts_table[i].set;
}
}
fprintf(stderr, "FAIL: invalid option\n");
exit(1);
}
static void usage(char *prog_name)
{
fprintf(stderr, "Usage: %s mount|umount <source> <target> [options]\n", prog_name);
fprintf(stderr, "Options are:\n");
fprintf(stderr, "-o flags sent to the mount syscall\n");
fprintf(stderr, "-d data sent to the mount syscall\n");
exit(1);
}
int main(int argc, char *argv[])
{
if (argc != 4) {
fprintf(stderr, "usage: %s [mount|umount] loopdev mountpoint\n",
argv[0]);
return 1;
char *options = NULL;
char *data = NULL;
int index;
int c;
char *op, *source, *target, *token;
unsigned long flags = 0;
while ((c = getopt (argc, argv, "o:d:h")) != -1) {
switch (c)
{
case 'o':
options = optarg;
break;
case 'd':
data = optarg;
break;
case 'h':
usage(argv[0]);
break;
default:
break;
}
}
if (strcmp(argv[1], "mount") == 0) {
if (mount(argv[2], argv[3], "ext2", 0xc0ed0000 | MS_NODEV, NULL ) == -1) {
index = optind;
if (argc - optind < 3) {
fprintf(stderr, "FAIL: missing positional arguments\n");
usage(argv[0]);
}
op = argv[index++];
source = argv[index++];
target = argv[index++];
if (options) {
token = strtok(options, ",");
while (token) {
flags |= get_mnt_opt_bit(token);
token = strtok(NULL, ",");
}
}
if (strcmp(op, "mount") == 0) {
if (mount(source, target, "ext2", flags, data) == -1) {
fprintf(stderr, "FAIL: mount %s on %s failed - %s\n",
argv[2], argv[3],
strerror(errno));
source, target, strerror(errno));
return errno;
}
} else if (strcmp(argv[1], "umount") == 0) {
if (umount(argv[3]) == -1) {
} else if (strcmp(op, "umount") == 0) {
if (umount(target) == -1) {
fprintf(stderr, "FAIL: umount %s failed - %s\n",
argv[3],
strerror(errno));
target, strerror(errno));
return errno;
}
} else {

View File

@@ -28,9 +28,11 @@ bin=$pwd
mount_file=$tmpdir/mountfile
mount_point=$tmpdir/mountpoint
mount_point2=$tmpdir/mountpoint2
mount_bad=$tmpdir/mountbad
loop_device="unset"
fstype="ext2"
root_was_shared="no"
setup_mnt() {
/bin/mount -n -t${fstype} ${loop_device} ${mount_point}
@@ -41,6 +43,10 @@ remove_mnt() {
if [ $? -eq 0 ] ; then
/bin/umount -t${fstype} ${mount_point}
fi
mountpoint -q "${mount_point2}"
if [ $? -eq 0 ] ; then
/bin/umount -t${fstype} ${mount_point2}
fi
mountpoint -q "${mount_bad}"
if [ $? -eq 0 ] ; then
/bin/umount -t${fstype} ${mount_bad}
@@ -53,12 +59,16 @@ mount_cleanup() {
then
/sbin/losetup -d ${loop_device} &> /dev/null
fi
if [ "${root_was_shared}" = "yes" ] ; then
mount --make-shared /
fi
}
do_onexit="mount_cleanup"
dd if=/dev/zero of=${mount_file} bs=1024 count=512 2> /dev/null
/sbin/mkfs -t${fstype} -F ${mount_file} > /dev/null 2> /dev/null
/bin/mkdir ${mount_point}
/bin/mkdir ${mount_point2}
/bin/mkdir ${mount_bad}
# in a modular udev world, the devices won't exist until the loopback
@@ -71,6 +81,195 @@ fi
loop_device=$(losetup -f) || fatalerror 'Unable to find a free loop device'
/sbin/losetup "$loop_device" ${mount_file} > /dev/null 2> /dev/null
# systemd mounts / and everything under it MS_SHARED which does
# not work with "move", so attempt to detect it, and remount /
# MS_PRIVATE temporarily. snippet from pivot_root.sh
FINDMNT=/bin/findmnt
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no PROPAGATION / > /dev/null 2>&1 ; then
if [ "$(${FINDMNT} -no PROPAGATION /)" == "shared" ] ; then
root_was_shared="yes"
fi
elif [ "$(ps hp1 -ocomm)" = "systemd" ] ; then
# no findmnt or findmnt doesn't know the PROPAGATION column,
# but init is systemd so assume rootfs is shared
root_was_shared="yes"
fi
if [ "${root_was_shared}" = "yes" ] ; then
mount --make-private /
fi
options=(
# default and non-default options
"rw,ro"
"exec,noexec"
"suid,nosuid"
"dev,nodev"
"async,sync"
"loud,silent"
"nomand,mand"
"atime,noatime"
"noiversion,iversion"
"diratime,nodiratime"
"nostrictatime,strictatime"
"norelatime,relatime"
"nodirsync,dirsync"
"noacl,acl"
)
# Options added in newer kernels
new_options=(
"nolazytime,lazytime"
"symfollow,nosymfollow"
)
prop_options=(
"unbindable"
"runbindable"
"private"
"rprivate"
"slave"
"rslave"
"shared"
"rshared"
)
combinations=()
setup_all_combinations() {
n=${#options[@]}
for (( i = 1; i < (1 << n); i++ )); do
list=()
for (( j = 0; j < n; j++ )); do
if (( (1 << j) & i )); then
current_options="${options[j]}"
nondefault=${current_options#*,}
list+=("$nondefault")
fi
done
combination=$(IFS=,; printf "%s" "${list[*]}")
combinations+=($combination)
done
}
run_all_combinations_test() {
for combination in "${combinations[@]}"; do
if [ "$(parser_supports "mount options=($combination),")" = "true" ] ; then
genprofile cap:sys_admin "mount:options=($combination)"
runchecktest "MOUNT (confined cap mount combination pass test $combination)" pass mount ${loop_device} ${mount_point} -o $combination
remove_mnt
genprofile cap:sys_admin "mount:ALL" "qual=deny:mount:options=($combination)"
runchecktest "MOUNT (confined cap mount combination deny test $combination)" fail mount ${loop_device} ${mount_point} -o $combination
remove_mnt
else
echo " not supported by parser - skipping mount option=($combination),"
fi
genprofile cap:sys_admin "mount:options=(rw)"
runchecktest "MOUNT (confined cap mount combination fail test $combination)" fail mount ${loop_device} ${mount_point} -o $combination
remove_mnt
done
}
test_nonfs_options() {
if [ "$(parser_supports "mount options=($1),")" != "true" ] ; then
echo " not supported by parser - skipping mount options=($1),"
return
fi
genprofile cap:sys_admin "mount:options=($1)"
runchecktest "MOUNT (confined cap mount $1)" pass mount ${loop_device} ${mount_point} -o $1
remove_mnt
genprofile cap:sys_admin "mount:ALL" "qual=deny:mount:options=($1)"
runchecktest "MOUNT (confined cap mount deny $1)" fail mount ${loop_device} ${mount_point} -o $1
remove_mnt
genprofile cap:sys_admin "mount:options=($1)"
runchecktest "MOUNT (confined cap mount bad option $2)" fail mount ${loop_device} ${mount_point} -o $2
remove_mnt
}
test_dir_options() {
if [ "$(parser_supports "mount options=($1),")" != "true" ] ; then
echo " not supported by parser - skipping mount option=($1),"
return
fi
genprofile cap:sys_admin "mount:ALL"
runchecktest "MOUNT (confined cap mount dir setup $1)" pass mount ${loop_device} ${mount_point}
genprofile cap:sys_admin "mount:options=($1)"
runchecktest "MOUNT (confined cap mount dir $1)" pass mount ${mount_point} ${mount_point2} -o $1
remove_mnt
genprofile cap:sys_admin "mount:ALL" "qual=deny:mount:options=($1)"
runchecktest "MOUNT (confined cap mount dir setup 2 $1)" pass mount ${loop_device} ${mount_point}
runchecktest "MOUNT (confined cap mount dir deny $1)" fail mount ${mount_point} ${mount_point2} -o $1
remove_mnt
}
test_propagation_options() {
if [ "$(parser_supports "mount options=($1),")" != "true" ] ; then
echo " not supported by parser - skipping mount option=($1),"
return
fi
genprofile cap:sys_admin "mount:ALL"
runchecktest "MOUNT (confined cap mount propagation setup $1)" pass mount ${loop_device} ${mount_point}
genprofile cap:sys_admin "mount:options=($1)"
runchecktest "MOUNT (confined cap mount propagation $1)" pass mount none ${mount_point} -o $1
remove_mnt
genprofile cap:sys_admin "mount:ALL" "qual=deny:mount:options=($1)"
runchecktest "MOUNT (confined cap mount propagation deny setup 2 $1)" pass mount ${loop_device} ${mount_point}
runchecktest "MOUNT (confined cap mount propagation deny $1)" fail mount none ${mount_point} -o $1
remove_mnt
}
test_remount() {
# setup by mounting first
genprofile cap:sys_admin "mount:ALL"
runchecktest "MOUNT (confined cap mount remount setup)" pass mount ${loop_device} ${mount_point}
genprofile cap:sys_admin "mount:options=(remount)"
runchecktest "MOUNT (confined cap mount remount option)" pass mount ${loop_device} ${mount_point} -o remount
genprofile cap:sys_admin "remount:ALL"
runchecktest "MOUNT (confined cap mount remount)" pass mount ${loop_device} ${mount_point} -o remount
genprofile cap:sys_admin "mount:ALL" "qual=deny:mount:options=(remount)"
runchecktest "MOUNT (confined cap mount remount deny option)" fail mount ${loop_device} ${mount_point} -o remount
genprofile cap:sys_admin "qual=deny:remount:ALL"
runchecktest "MOUNT (confined cap mount remount deny)" fail mount ${loop_device} ${mount_point} -o remount
# TODO: add test for remount options
remove_mnt
}
test_options() {
for i in "${options[@]}"; do
default="${i%,*}"
nondefault="${i#*,}"
test_nonfs_options $default $nondefault
test_nonfs_options $nondefault $default
done
for i in "bind" "rbind" "move"; do
test_dir_options $i
done
for i in "${prop_options[@]}"; do
test_propagation_options $i
done
test_remount
# the following combinations tests take a long time to complete
# setup_all_combinations
# run_all_combinations_test
}
# TEST 1. Make sure can mount and umount unconfined
runchecktest "MOUNT (unconfined)" pass mount ${loop_device} ${mount_point}
@@ -80,6 +279,43 @@ setup_mnt
runchecktest "UMOUNT (unconfined)" pass umount ${loop_device} ${mount_point}
remove_mnt
# Check mount options that may not be available on this kernel
for i in "${new_options[@]}"; do
default="${i%,*}"
if "$bin/mount" mount ${loop_device} ${mount_point} -o $default > /dev/null 2>&1; then
remove_mnt
options+=($i)
else
echo " not supported by kernel - skipping mount options=($i),"
fi
done
for i in "${options[@]}"; do
default="${i%,*}"
nondefault="${i#*,}"
runchecktest "MOUNT (unconfined mount $default)" pass mount ${loop_device} ${mount_point} -o $default
remove_mnt
runchecktest "MOUNT (unconfined mount $nondefault)" pass mount ${loop_device} ${mount_point} -o $nondefault
remove_mnt
done
for i in "bind" "rbind" "move"; do
runchecktest "MOUNT (unconfined mount setup $i)" pass mount ${loop_device} ${mount_point}
runchecktest "MOUNT (unconfined mount $i)" pass mount ${mount_point} ${mount_point2} -o $i
remove_mnt
done
for i in "${prop_options[@]}"; do
runchecktest "MOUNT (unconfined mount dir setup $i)" pass mount ${loop_device} ${mount_point}
runchecktest "MOUNT (unconfined mount dir $i)" pass mount none ${mount_point} -o $i
remove_mnt
done
runchecktest "MOUNT (unconfined mount remount setup)" pass mount ${loop_device} ${mount_point}
runchecktest "MOUNT (unconfined mount remount)" pass mount ${loop_device} ${mount_point} -o remount
remove_mnt
# TEST A2. confine MOUNT no perms
genprofile
runchecktest "MOUNT (confined no perm)" fail mount ${loop_device} ${mount_point}
@@ -91,6 +327,7 @@ remove_mnt
if [ "$(kernel_features mount)" != "true" -o "$(parser_supports 'mount,')" != "true" ] ; then
echo " mount rules not supported, using capability check ..."
genprofile capability:sys_admin
runchecktest "MOUNT (confined cap)" pass mount ${loop_device} ${mount_point}
remove_mnt
@@ -157,6 +394,7 @@ else
runchecktest "UMOUNT (confined cap umount:ALL)" pass umount ${loop_device} ${mount_point}
remove_mnt
test_options
fi
#need tests for move mount, remount, bind mount, chroot
#need tests for chroot

View File

@@ -38,7 +38,7 @@ badchild=r
# Add genprofile params that are common to all hats here
common=""
if [ "$(kernel_features signal)" == "true" -a "$(parser_supports 'signal,')" == "true" ] ; then
if [ "$(kernel_features signal)" = "true" -a "$(parser_supports 'signal,')" = "true" ] ; then
# Allow send/receive of all signals
common="${common} signal:ALL"
fi

View File

@@ -44,7 +44,7 @@ do_test()
shift 4
desc="ONEXEC $desc ($prof -> $target_prof)"
if [ "$target_prof" == "nochange" ] ; then
if [ "$target_prof" = "nochange" ] ; then
runchecktest "$desc" $res -l "$prof" -- "$@"
else
runchecktest "$desc" $res -O "$target_prof" -l "$prof" -L "$target_prof" -- "$@"

View File

@@ -55,7 +55,7 @@ fi
# MS_PRIVATE temporarily.
FINDMNT=/bin/findmnt
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no PROPAGATION / > /dev/null 2>&1 ; then
if [ "$(${FINDMNT} -no PROPAGATION /)" == "shared" ] ; then
if [ "$(${FINDMNT} -no PROPAGATION /)" = "shared" ] ; then
root_was_shared="yes"
fi
elif [ "$(ps hp1 -ocomm)" = "systemd" ] ; then

View File

@@ -86,6 +86,19 @@ requires_kernel_features()
fi
}
requires_any_of_kernel_features()
{
while [ $# -gt 0 ]; do
local res=$(kernel_features "$1")
if [ "$res" = "true" ] ; then
return 0;
fi
shift
done
echo "$res. Skipping tests ..."
exit 0
}
# requires_namespace_interface() - exit if namespace interface is not available
requires_namespace_interface()
{

View File

@@ -55,7 +55,7 @@ runchecktest "test 2 -h prog" pass -h -n 100 $helper ${bin_true}
runchecktest "test 2 -hc prog" pass -h -c -n 100 $helper ${bin_true}
if [ "$(kernel_features ptrace)" == "true" -a "$(parser_supports 'ptrace,')" == "true" ] ; then
if [ "$(kernel_features ptrace)" = "true" -a "$(parser_supports 'ptrace,')" = "true" ] ; then
. $bin/ptrace_v6.inc
else
. $bin/ptrace_v5.inc

View File

@@ -93,7 +93,7 @@ querytest()
runchecktest "$desc" "$pf" "$expect" "$label" "$perms" $*
}
if [ "$(kernel_features dbus)" == "true" ]; then
if [ "$(kernel_features dbus)" = "true" ]; then
# Check querying of a label that the kernel doesn't know about
# aa_query_label() should return an error
expect anything
@@ -227,7 +227,7 @@ fi
genqueryprofile "file,"
expect allow
perms file exec,write,read,append,create,delete,setattr,getattr,chmod,chown,link,linksubset,lock,exec_mmap
if [ "$(kernel_features query/label/multi_transaction)" == "true" ] ; then
if [ "$(kernel_features query/label/multi_transaction)" = "true" ] ; then
querytest "QUERY file (all base perms #1)" pass /anything
querytest "QUERY file (all base perms #2)" pass /everything
else

View File

@@ -37,7 +37,7 @@ af_unix_create_label=""
af_unix_inherit=""
aa_enabled="/sys/module/apparmor/parameters/enabled:r"
if [ "$(kernel_features network/af_unix)" == "true" -a "$(parser_supports 'unix,')" == "true" ]; then
if [ "$(kernel_features network/af_unix)" = "true" -a "$(parser_supports 'unix,')" = "true" ]; then
# AppArmor requires that the process inheriting the sock file
# descriptors have send,receive perms in its profile
af_unix_create="unix:(create,getopt)"

View File

@@ -29,7 +29,7 @@ bin=$pwd
# check if we can run the test at all
fstype=$(stat -f --format '%T' "${tmpdir}")
if [ "${fstype}" == "tmpfs" ] ; then
if [ "${fstype}" = "tmpfs" ] ; then
echo "ERROR: tmpdir '${tmpdir}' is of type tmpfs; can't mount a swapfile on it" 1>&2
echo "ERROR: skipping swap tests" 1>&2
num_testfailures=1

View File

@@ -27,7 +27,9 @@ okperm=rw
badperm=w
af_unix=""
if [ "$(kernel_features network/af_unix)" == "true" -a "$(parser_supports 'unix,')" == "true" ]; then
if [ "$(kernel_features network_v8)" = "true" -a "$(parser_supports 'unix,')" = "true" ]; then
af_unix="unix:create"
elif [ "$(kernel_features network/af_unix)" = "true" -a "$(parser_supports 'unix,')" = "true" ]; then
af_unix="unix:create"
fi
@@ -137,7 +139,7 @@ runchecktest "fd passing; confined -> confined (no perm)" fail $file $fd_client
sleep 1
rm -f ${socket}
if [ "$(kernel_features policy/network/af_unix)" == "true" -a "$(parser_supports 'unix,')" == "true" ] ; then
if [ "$(kernel_features policy/network/af_unix)" = "true" -a "$(parser_supports 'unix,')" = "true" ] ; then
# FAIL - confined client, no access to the socket file
genprofile $file:$okperm $af_unix $socket:rw $fd_client:px -- image=$fd_client $file:$okperm $af_unix

View File

@@ -29,7 +29,7 @@ bin=$pwd
. $bin/prologue.inc
requires_kernel_features policy/versions/v6
#af_mask for downgrade test af_unix for full test
requires_kernel_features network/af_mask
requires_any_of_kernel_features network/af_mask network_v8/af_mask
settest unix_socket
@@ -43,9 +43,9 @@ message=4a0c83d87aaa7afa2baab5df3ee4df630f0046d5bfb7a3080c550b721f401b3b\
okserver=w
badserver1=r
badserver2=
if [ "$(kernel_features policy/versions/v7)" == "true" ] ; then
if [ "$(kernel_features policy/versions/v7)" = "true" ] ; then
okserver=rw
badserver2=w
# badserver2=w
fi
# af_unix support requires 'unix create' to call socket()
@@ -54,9 +54,16 @@ fi
# af_unix support requires 'unix getattr' to call getsockname()
af_unix_okserver=
af_unix_okclient=
if [ "$(kernel_features network/af_unix)" == "true" -a "$(parser_supports 'unix,')" == "true" ] ; then
if ( [ "$(kernel_features network_v8/af_unix)" = "true" ] ||
[ "$(kernel_features network/af_unix)" = "true" ] ) &&
[ "$(parser_supports 'unix,')" = "true" ] ; then
af_unix_okserver="create,setopt"
af_unix_okclient="create,getopt,setopt,getattr"
elif [ "$(kernel_features network_v8)" = "true" ] ; then
# af_unix_okserver="create,setopt"
# af_unix_okclient="create,getopt,setopt,getattr"
af_unix_okserver="create"
af_unix_okclient="create"
fi
okclient=rw
@@ -88,7 +95,7 @@ testsocktype()
# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1373176
# get resolved
local ex_result="pass"
if [ "${socktype}" == "dgram" ] ; then
if [ "${socktype}" = "dgram" ] ; then
ex_result="xpass"
fi

View File

@@ -1038,7 +1038,12 @@ def ask_exec(hashlog):
hashlog[aamode][target_profile]['final_name'] = exec_target
# Check profile exists for px
if not os.path.exists(get_profile_filename_from_attachment(exec_target, True)):
if exec_target.startswith(('/', '@', '{')):
prof_filename = get_profile_filename_from_attachment(exec_target, True)
else: # named exec
prof_filename = get_profile_filename_from_profile_name(exec_target, True)
if not os.path.exists(prof_filename):
ynans = 'y'
if 'i' in exec_mode:
ynans = aaui.UI_YesNo(_('A profile for %s does not exist.\nDo you want to create one?') % exec_target, 'n')

View File

@@ -207,6 +207,9 @@ class ReadLog:
# in current log style, owner permissions are indicated by a match of fsuid and ouid
owner = True
if 'x' in dmask and dmask != 'x':
dmask = dmask.replace('x', '') # if dmask contains x and another mode, drop x here - we should see a separate exec event
for perm in dmask:
if perm in 'mrwalk': # intentionally not allowing 'x' here
self.hashlog[aamode][full_profile]['path'][e['name']][owner][perm] = True

View File

@@ -76,6 +76,10 @@ exception_not_raised = (
'file/bad_re_brace_1.sd',
'file/bad_re_brace_2.sd',
'file/bad_re_brace_3.sd',
'mount/bad_1.sd',
'mount/bad_2.sd',
'mount/bad_3.sd',
'mount/bad_4.sd',
'mount/bad_opt_10.sd',
'mount/bad_opt_11.sd',
'mount/bad_opt_12.sd',
@@ -104,6 +108,9 @@ exception_not_raised = (
'mount/bad_opt_26.sd',
'mount/bad_opt_27.sd',
'mount/bad_opt_28.sd',
'mount/bad_opt_29.sd',
'mount/bad_opt_30.sd',
'mount/bad_opt_31.sd',
'profile/flags/flags_bad10.sd',
'profile/flags/flags_bad11.sd',
'profile/flags/flags_bad12.sd',