I’m trying to build a snap for sc-controller, the application which allows Steam Controllers to function with non-Steam games on Linux. It acts as an emulation layer, translating input from the Steam Controller into keypresses, xbox360 axes etc.
My snap runs in devmode but I’d like to get it running as strict. With no plugs defined I see apparmor DENIED errors for /sys/bus/usb/devices (read) and /proc/uinput (rw) with a gui error about not being able to create the uinput device. If I connect the raw-usb plug I don’t see either denial in the logs but the app gui yields an error message saying it cannot create the uinput device. If I connect hardware-observe but disconnect raw-usb I get an error about not being able to access the USB device in the gui.
So, it looks as if I need raw-usb, but what do I need to get access to /dev/uinput?
I don’t see any interface for /dev/uinput in the snapd codebase. Is this something which needs a new interface? Is there any way I can test that before making an interface request? Or is there something else I’m missing here (e.g. is this something related to udev rules etc)
There isn’t an interface for /dev/uinput at this time. It could be added, but it would be very privileged. Unfortunately, the steam controller udev rules typically set the permissions as 0666 but this is way too lax for what the device allows (it would allow any process on the system to inject input events, to say, drive the desktop). Due to how the interface works, this is an all or nothing deal with no fine-grained mediation (see https://www.kernel.org/doc/html/v4.12/input/uinput.html for details).
Put more simply, while we could certainly create an interface for accessing uinput, that interface would not create udev rules to allow normal users to access it for security reasons. Unfortunately, this seems to be precisely what a steam controller needs. Snapd could set up the device with an ACL (use case 2 of Multiple users and groups in snaps), but this is not currently supported. The best that could be done at this time would be to create the uinput interface for the security policy to allow use, but not touch udev rules for setting permissions on the device. In this manner, a snap could access the steam controller provided the user setup the permissions of the uinput device.
Thanks @jdstrand, for the very detailed explanation. This is real foreign territory for my Linux knowledge.
I’d anticipate the udev rules would be set up as they are just now - by installing the steam-devices .deb from the archive. This is required to use the Steam Controller natively through Steam anyway, and there’s no problem installing this on 20.04. I wouldn’t be intending to package that as part of the snap; nor would that mechanism be a shock to users as they’ve had to do this already on 18.04 using the sc-controller .deb. My rationale for packaging this is simply to keep sc-controller running on 20.04, where some necessary python2 packages have been removed.
Thus, if there was an interface to /dev/uinput and the user had manually installed the udev rules from the steam-devices deb package, would this be enough to get this working under some degree of confinement? Is there any way I can experiment with this on my own machine?
This should be enough, yes. You can test this by installing your snap and connect any existing interfaces you have, then modify /var/lib/snapd/apparmor/profiles/snap.yoursnap.cmd to have:
/dev/uinput rw,
then load the updated apparmor rules into the kernel:
If you don’t have /etc/udev.d/rules.d/70-snap.yoursnap.rules on your system, you can test your snap now. If you do, add to it:
# test
KERNEL=="uinput", TAG+="snap_yoursnap_cmd"
then run:
$ sudo udevadm trigger --subsystem-match=misc # make the rules apply
$ snap run --shell yoursnap.cmd # launch a shell under yoursnap.cmd
then in another terminal run udevadm and check TAGS:
$ udevadm info /dev/uinput
...
E: TAGS=:uaccess:snap_yoursnap_cmd:seat:
if yoursnap_cmd is listed under TAGS, then things are working properly. You can exit the snap run --shell command and test your snap. Please note, that snapd will undo these from time to time so you might have to reapply the changes when testing.
I’ve added a TODO to add this interface in the next batch of updates.
It missed 2.45 (sorry). I do plan to implement it next week so it will be in 2.46. It is possible it could be pulled into 2.45.1 depending on the timing, or 2.45.2 if there is one. I’ll suggest it in the PR (and I’ll post the PR here when I submit it so you can follow along there).
It is a shorthand for a pattern of interfaces. Specifically, there is both an installation constraint and an auto-connection constraint. One way to think about it is that most interfaces are either unprivileged (eg, auto-connected) or privileged (eg, manually connected). Superprivileged are those that are in some manner deemed so privileged that a snap shouldn’t even be installable from the store if it plugs it without a corresponding snap declaration that allows installation and the PR is discussing which it should be.
In terms of your snap, if it was deemed superprivileged then there would simply be additional questions during the request to use/auto-connect the interface.
Thanks for the explanation. I can’t say I can see the logic in a uinput interface being superprivileged but home, network and x11 being unprivileged but I’m enjoying following along