Software based presentation remote [or need an interface to talk uinput]

I am working on a presentation remote app as part of the deskconn project. A daemon would run on the desktop and the Android device on the local network would be able to send key events (pageup, pagedown, esc, etc), practically allowing my phone to control a Libreoffice Impress slideshow.

I could use a X11 specific functionality (maybe steal some code from autopilot) that would work, but it won’t work under wayland.

Since sending key events is a very sensitive issue in terms of software security, I believe it may make sense to create a snapd interface that allows only a limited set of key events to be forwarded or add a layer like the GPIO interface does i.e. allowing only specific keys requested (and justified) by a requesting app. Not really sure if that is possible with the uinput API as of today or if snapd would need to develop something homebrewed.

cc @jdstrand @zyga

Writing a longer line to bump.

Can you tell me how keys would be injected into the system? A link / reference into uinput would be great. As for the interface and any potential filtering. I think that unless the kernel side does something perfect we’d need a trusted helper to expose a socket to send events to, the trusted helper would reference snapd policy and inject the keys into the system using the lower-level interface.

I use python-evdev to talk to uinput, below is a sample code that is able to send key events for my app, this under a confined snap does not work.

The only python dependency is evdev

pip install evdev

Here is the working example to send key events

import time

from evdev import uinput, ecodes


class Slides:
    def __init__(self):
        self.device = uinput.UInput()

    def _press_and_release(self, key):
        self.device.write(ecodes.EV_KEY, key, 1)
        time.sleep(0.1)
        self.device.write(ecodes.EV_KEY, key, 0)
        self.device.syn()

    def next(self):
        self._press_and_release(ecodes.KEY_PAGEDOWN)

    def previous(self):
        self._press_and_release(ecodes.KEY_PAGEUP)

    def start(self):
        self._press_and_release(ecodes.KEY_F5)

    def end(self):
        self._press_and_release(ecodes.KEY_ESC)

    def cleanup(self):
        self.device.close()


if __name__ == '__main__':
    slides = Slides()
    # Will send page down event
    slides.next()
    slides.cleanup()

This is documented in https://www.kernel.org/doc/html/latest/input/uinput.html and there is an existing bug: https://bugs.launchpad.net/snappy/+bug/1622639/comments/1

Going by the documentation only, the new ioctl interface would theoretically allow seccomp arg filtering on the ioctl syscall. In practice, this is going to be difficult due to our current ioctl rule (ioctl - !TIOCSTI). Basically, ioctl mediation is problematic since ioctl is a generic interface for setting arbitrary device parameters for arbitrary devices making robust and regression-free ioctl mediation difficult. There are some ideas for better ioctl mediation via apparmor, but this in on the long term roadmap. Today, our ioctl mediation is primarily around Linux capabilities (the one exception being the seccomp rule which says, “everything but TIOCSTI is allowed”).

That said, the kernel also supports the historic uinput_user_dev struct implementation, which is unmediatable with seccomp arg filtering anyway.

I think the only path forward is my suggestion in the bug: “I suspect if this were to be implemented as an interface, we would likely call it ‘input-control’, and it would not auto-connect. Then in the future fine-grained mediation for uinput could be explored.