Race in load-module snap policy check in classic confinement
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
pulseaudio (Ubuntu) |
Fix Released
|
High
|
James Henstridge |
Bug Description
SUMMARY
=========
When running a snap in classic confinement, that needs access to PA_COMMAND_
After running "pacmd unload-module module-snap-policy" and unloading the snap policy module, these work reliably.
I have verified this in a fresh install of Ubuntu 20.04 in a VM.
STEPS TO REPRODUCE
=========
a) Either build a snap with classic confinement that sends these commands on the pulseaudio native protocol socket. (This is how I found the bug)
b) Or, what I did here to easier reproduce, abuse the sandbox of a random classic snap:
Download the attached bug.tgz with a minimal reproducer. It contains the source code for a program that sends load and unload commands to pulse. Unfortunately `pacmd` has a pid-file check that fails inside the sandbox and doesn't work. The reproducer does essentially the same as "pacmd load/unload-module" though.
(a pre-compiled x64 binary is also included in case you don't have a go compiler and dare to run an untrusted binary in a VM)
Unpack the tgz, build it, if necessary with "go mod download && go build"
Grab a random classic mode snap to use its sandbox as a test bed:
$ sudo snap install atom --classic
atom 1.48.0 from Snapcrafters installed
Open a shell in its sandbox:
snap run --shell atom
Navigate to the compiled binary and execute it a few times:
user@
2020/07/08 18:46:10 PulseAudio connection created successfully
2020/07/08 18:46:10 Couldn't load module, error message: PulseAudio error: commandLoadModule -> Access denied
user@
2020/07/08 18:46:11 PulseAudio connection created successfully
Loaded Module sucessfully at index: 40
user@
2020/07/08 18:46:12 PulseAudio connection created successfully
Loaded Module sucessfully at index: 41
user@
2020/07/08 18:46:12 PulseAudio connection created successfully
2020/07/08 18:46:12 Couldn't load module, error message: PulseAudio error: commandLoadModule -> Access denied
user@
2020/07/08 18:46:14 PulseAudio connection created successfully
2020/07/08 18:46:14 Couldn't load module, error message: PulseAudio error: commandLoadModule -> Access denied
user@
2020/07/08 18:46:14 PulseAudio connection created successfully
2020/07/08 18:46:14 Couldn't load module, error message: PulseAudio error: commandLoadModule -> Access denied
user@
2020/07/08 18:46:15 PulseAudio connection created successfully
2020/07/08 18:46:15 Couldn't load module, error message: PulseAudio error: commandLoadModule -> Access denied
Succeeds and fails apparently at random.
Now from a non-sandboxed shell, run
pacmd unload-module module-snap-policy
to unload the snap-policy module from pulseaudio, now run ./bug a few more times. It now succeeds reliably, every time.
Side note, with the real program on my actual machine, the race seems to behave slightly differently. It seems not to work the first time an application is started, but closing it and reopening it seems to make it work pretty reliably afterwards. Restarting "snapd", causes the following run the snap to fail again.
EXPECTED BEHAVIOUR
==================
The pulseaudio snap policy module should correctly determine and enforce it's policy.
ACTUAL BEHAVIOUR
================
The pulseaudio snap policy module seemingly at random denies access when the snap has the permissions to do an operation.
ADDITIONAL INFORMATION
=======
$ lsb_release -rd
Description: Ubuntu 20.04 LTS
Release: 20.04
$ apt-cache policy pulseaudio
pulseaudio:
Installed: 1:13.99.
Candidate: 1:13.99.
Version table:
*** 1:13.99.
500 http://
100 /var/lib/
1:
500 http://
1:
500 http://
CVE References
tags: | added: rls-ff-incoming |
Changed in pulseaudio (Ubuntu): | |
assignee: | nobody → James Henstridge (jamesh) |
tags: | added: focal |
Changed in pulseaudio (Ubuntu): | |
importance: | Undecided → High |
tags: | removed: rls-ff-incoming |
I think there's two issues at play here.
The hooks we added for module loading/unloading as part of USN-4355-1 simply check if the client has an AppArmor label that looks like it belongs to a snap and denies access if found. This will also deny access to classic snaps, which is probably a mistake.
The race condition you've encountered is probably a case of "policy module not in effect" vs. "policy module in effect" rather than a race in the behaviour of the policy module itself. This probably indicates that Pulse is servicing client requests before it has completely started.
For the first issue, we can make the hook request info about the snap and allow access to classic snaps. For the second, I think we just need to load module-snap-policy earlier during start up.