Adding a "snapctl is-connected --pid" command

I’m starting this thread to break out discussion from Autoconnect requests for pulseaudio on the subject of a new command intended to let snapped daemons that communicate with other snapped applications to check whether a particular interface connection exists.

@pedronis made the following proposal:

The process ID version of this could let a daemon listening on a unix domain socket use SO_PEERCRED to check if the client it is talking to has connected a particular interface. A D-Bus daemon can do similar using the GetConnectionUnixProcessID method call to the bus daemon.

Specific use cases include:

  1. The pulseaudio snap does not currently provide the newer audio-playback and audio-record slots expected by modern snaps. A command like this would allow the snap to implement similar microphone access restrictions to Ubuntu’s current PA packages, without plugging snapd-control.

  2. The snap version of CUPS would like to provide two tiers of access to clients, so it can accept print jobs from some snapped clients without letting them reconfigure the server. When the new cups interface lands, the snap will need to be able to check whether clients have also plugged cups-control before allowing access to privileged APIs. Ideally it should be able to do this without snapd-control access.

I’ve put a prototype of the --pid variant of the proposal here:

https://github.com/snapcore/snapd/pull/9132

A test build of snapd can be found in the CI artifacts here: https://github.com/snapcore/snapd/suites/1038877427/artifacts/13844140

There are two main areas that need to be addressed before this would be suitable to merge:

  1. we probably don’t want to expose this functionality to every snap, since it exposes potentially sensitive information about the system to sandboxed apps. One option would be to tie the use to particular interfaces.

    For example: if the interface could only be called with an audio-record interface slot, then you would first need permission to publish snaps with that slot in order to use it. That might be easier than linking the permission to the snap name directly.

  2. On classic systems, the daemon will likely need to communicate with more than just strict confined snaps:

    • unconfined apps on the host system.
    • snaps with classic confinement (@till.kamppeter pointed this one out in the PR’s comments).

    For the use cases mentioned above, it would make sense to treat such processes the same as those belonging to a connected snap. It does muddy the waters when we include classic snaps here though, since they could potentially have plugs and slots which may or may not be connected.

I’ve left the --security-tag version of the command unimplemented, since I’m not convinced it is necessary. In most cases, the daemon will need to know the peer’s process ID first in order to determine the security label, and the /proc access required to look up an arbitrary process’s security label reveals a lot about the system.

Based on a suggestion from Samuele, we could solve (2) by making use of more return codes than just 0 and 1. For example:

  • 0: the plug/slot is connected to the snap identified by $pid
  • 1: no connection, and $pid belongs to a strict confined snap
  • 2: no connection, and $pid belongs to a classic confined snap
  • 3: $pid is unconfined

This lets the caller treat the exit code as a boolean that is true only if there is a connection. Apps like CUPS that want to communicate with host system and classic confined apps can treat 0, 2, and 3 as success codes.