apparmor 'l' denial when using linkat() with attach_disconnected
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
AppArmor |
Confirmed
|
Medium
|
Unassigned |
Bug Description
Qt 5.10 recently (https:/
fd = open("/tmp", O_RDWR | O_DIRECTORY | O_CLOEXEC | O_TMPFILE)
write(fd, "...")
linkat(AT_FDCWD, "/proc/self/fd/<fd for O_TMPFILE>", AT_FDCWD, "/tmp/...", AT_SYMLINK_FOLLOW)
A diagnosis was provided here: https:/
Attached is a simple reproducer that demonstrates that apparmor works fine with this for unconfined and non-attach_
$ tar -zxvf ./linkat.tar.gz
linkat/
linkat/main.c
linkat/test.sh
linkat/profile
linkat/
linkat/
linkat/
$ cd ./linkat
$ ./test-no-snap.sh
= Test unconfined =
Running: aa-exec -p unconfined -- ./tmp-linkat /tmp
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
Running: aa-exec -p unconfined -- ./tmp-linkat /home/jamie/
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
= Test confined =
Loading apparmor profile for 'test'
Policy for 'test'
#include <tunables/global>
profile test {
#include <abstractions/base>
/tmp/{#,okular}* rwl,
@{HOME}
}
Running: aa-exec -p test -- ./tmp-linkat /tmp
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
Running: aa-exec -p test -- ./tmp-linkat /home/jamie/
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
= Test confined with attach_disconnected =
Loading apparmor profile for 'test_atch_
Policy for 'test_attach_
#include <tunables/global>
profile test_attach_
#include <abstractions/base>
/tmp/{#,okular}* rwl,
@{HOME}
}
Running: aa-exec -p test_attach_
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
Running: aa-exec -p test_attach_
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
FAIL: some tests failed
[1]
The failing test shows this in the logs:
May 18 13:53:29 localhost audit[20542]: AVC apparmor="DENIED" operation="link" info="Failed name lookup - deleted entry" error=-2 profile=
May 18 13:53:29 localhost audit[20542]: AVC apparmor="DENIED" operation="link" profile=
May 18 13:53:29 localhost audit[20543]: AVC apparmor="DENIED" operation="link" info="Failed name lookup - deleted entry" error=-2 profile=
May 18 13:53:29 localhost audit[20543]: AVC apparmor="DENIED" operation="link" profile=
Unfortunately, this breaks snaps using Qt 5.10 as there is no workaround (attach_
$ ./test.sh
= Test strict mode snap (per-snap mount namespace with strict) =
test-tmp-linkat 1.0 installed
Grepping for relevant policy:
@{SNAP_
profile "snap.test-
owner @{HOME}
/var/
/tmp/** mrwlkix,
Running: test-tmp-linkat /tmp
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
Running: test-tmp-linkat /home/jamie/
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
= Test devmode mode snap (per-snap mount namespace with complain) =
test-tmp-linkat 1.0 installed
Grepping for relevant policy:
@{SNAP_
profile "snap.test-
owner @{HOME}
/var/
/tmp/** mrwlkix,
Running: test-tmp-linkat /tmp
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
Running: test-tmp-linkat /home/jamie/
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
= Test class mode snap (global mount namespace loose with complain) =
test-tmp-linkat 1.0 installed
Grepping for relevant policy:
@{SNAP_
profile "snap.test-
/** rwlkm,
Running: test-tmp-linkat /tmp
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
Running: test-tmp-linkat /home/jamie/
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
= Test unconfined =
Running: aa-exec -p unconfined -- /snap/test-
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
Running: aa-exec -p unconfined -- /snap/test-
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
= Test confined outside of snap =
Loading apparmor profile for 'test'
Policy for 'test'
#include <tunables/global>
profile test {
#include <abstractions/base>
/tmp/{#,okular}* rwl,
@{HOME}
}
Running: aa-exec -p test -- /snap/test-
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
Running: aa-exec -p test -- /snap/test-
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat success
= Test confined outside of snap with attach_disconnected =
Loading apparmor profile for 'test_atch_
Policy for 'test_attach_
#include <tunables/global>
profile test_attach_
#include <abstractions/base>
/tmp/{#,okular}* rwl,
@{HOME}
}
Running: aa-exec -p test_attach_
prefix: /tmp
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
Running: aa-exec -p test_attach_
prefix: /home/jamie/
opened fd: 3 [/proc/self/fd/3]
writing works
linkat failed: No such file or directory
FAIL
FAIL: some tests failed
[1]
The reduced test case (ie, test-no-snap.sh) was confirmed to have this bug on:
* Linux version 4.16.0-1-amd64 (<email address hidden>) (gcc version 7.3.0 (Debian 7.3.0-17)) #1 SMP Debian 4.16.5-1 (2018-04-29)
* Ubuntu 4.15.0-
* Ubuntu 4.13.0-
* Ubuntu 4.4.0-121.
* Ubuntu 3.13.0-
Ubuntu 12.04 kernel does not support O_TMPFILE (it is 3.2 and O_TMPFILE was added in 3.11).
So it looks like apparmor is detecting one of the entries as deleted, which it will allow for fds being passed around but not to base lookups off of.
You should be able to work around this by adding the mediate_deleted flag to the profile. Which will allow path lookup from the fd that it is identifying as deleted.
So your example profile would change to
profile test_attach_ disconnected (attach_ disconnected, mediate_ deleted) {
#include <abstractions/base>
/tmp/{#,okular}* rwl, /snap/test- tmp-linkat/ common/ {#,okular} * rwl,
@{HOME}
}