Missing read access to pidfile in audio interfaces breaks pacmd

If pacmd is used from within a snap it completely fails to work.
At startup this utility checks for the pulseaudio pid by trying to read from /run/user/$UID/pulse/pid like:

    if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
        pa_log(_("No PulseAudio daemon running, or not running as session daemon."));
        goto quit;
    }

and later tries to send a SIGUSR2

        if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
            pa_log(_("Failed to kill PulseAudio daemon."));
            goto quit;
        }

could we add a:

owner /{,var/}run/pulse/pid r,

to the audio-record/audio-playback interfaces so this pulseaudio util can actually communicate with the daemon as it should ? @alexmurray, @jdstrand is there any security reason to not allow read access ?

Audio playback seems to have relevant /{,var}/run/* paths included. The docs mention that audio-record should be used alongside audio-playback so we seem to be covered there.

However, I’m not sure about kill(<pid>, SIGUSR2), this may need a line in the AppArmor profile. Perhaps something like:

signal (send) set=(usr2) peer=unconfined

Do you have the actual denial line by any chance?

The denial is:

Mär 09 10:56:59 anubis audit[14635]: AVC apparmor="DENIED" operation="open" profile="snap.zoom-client.zoom-client" name="/run/user/1000/pulse/pid" pid=14635 comm="pacmd" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
Mär 09 10:56:59 anubis kernel: audit: type=1400 audit(1583747819.907:278923): apparmor="DENIED" operation="open" profile="snap.zoom-client.zoom-client" name="/run/user/1000/pulse/pid" pid=14635 comm="pacmd" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000

And here is some shell

ogra@anubis:~$ snap run --shell zoom-client
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ogra@anubis:/home/ogra$ cat /run/user/1000/pulse/pid
cat: /run/user/1000/pulse/pid: Permission denied
ogra@anubis:/home/ogra$ ls -lh /run/user/1000/pulse/pid
-rw------- 1 ogra ogra 5 Feb 22 10:54 /run/user/1000/pulse/pid
ogra@anubis:/home/ogra$ XDG_RUNTIME_DIR="/run/user/1000" /snap/zoom-client/current/usr/bin/pacmd info
No PulseAudio daemon running, or not running as session daemon.
ogra@anubis:/home/ogra$ exit
ogra@anubis:~$ snap connections zoom-client|grep audio
audio-playback          zoom-client:audio-playback          :audio-playback          -
audio-record            zoom-client:audio-record            :audio-record            manual
ogra@anubis:~$

The cat as well as the pacmd call both trigger the denial reliably.

Do you what particular use zoom-client makes of pacmd?

I looked at the implementation. The pacmd binary tries to use /run/user/<uid>/pulse/cli to talk to the daemon. It uses a simple text protocol where commands are the commands you pass in the command line arguments. They also seem to match the commands used in the config files (eg. /etc/pulse/default.pa).

I’m a bit worried that pacmd opens up a lot of possibilities that perhaps should be mediated. Specifically I was able to unload module-snap-policy which checks that snap has the audio-record interface connected before allowing recording access. Which brings a question whether it should even be possible to unload that specific module. I’m sure @jdstrand will have some input here.

well, i guess pulseaudio kind of relies on the fact that you are the owner of the socket and the pulse process so there seems to be no added security on top of that.
pacmd is essentially giving you full control over the daemon.
perhaps this warrants a pulseaudio-control interface with the extra permission for the cli socket.
yet i think not being able to read the pid file is a bug.

typically apps would use (the way more limited) pactl instead of pacmd … but given that zoom is proprietray i doubt they’d change to it just for being snappable … OTOH it seems to operate fine even without pacmd (audio playback and record do work on both of my test systems, i was just digging into what causes the No PulseAudio daemon running, or not running as session daemon. message it spills on startup).

To answer the original question, /run/user/*/pulse/pid is just a regular file containing the PID of the pulseaudio daemon - allowing read access to this should be fine from a security point-of-view given that the base AppArmor policy for snaps already provides a lot of access to /proc so strictly confined snaps can just walk /proc/<pid> to try and find this out anyway an addition of:

owner /{,var/}run/user/*/pulse/pid r,

would not have any additional security impact.

1 Like

There is ambiguity in this request where this is requested: owner /{,var/}run/pulse/pid r, and then later owner /{,var/}run/user/*/pulse/pid r, is suggested to be added. The former would be for a system-wide pulseaudio and the latter for the user’s session. I think the request is actually for:

/{,var/}run/pulse/pid r,
owner /{,var/}run/user/*/pulse/pid r,

These accesses are fine to allow since the pid can already be obtained via the ‘ps’ using only the default template, but please note that pacmd uses $XDG_RUNTIME_DIR/pulse/cli for the socket instead of $XDG_RUNTIME_DIR/pulse/native which is allowed by the audio-* interfaces, so while these pid accesses can be added, $XDG_RUNTIME_DIR/pulse/cli will not since there are no mediation hooks for that. We may decide to update the now deprecated pulseaudio interface for other accesses (or introduce a new pulseaudio-control interface) at some future date.

1 Like

It looks like the policy has owner match for /{,var/}run/pulse/* accesses so we should keep it for the pid.

Fyi, https://github.com/snapcore/snapd/pull/8793

1 Like