Markdown Edit

Evdev Mouse Click Telemetry Security on Wayland

Wayland screen recording tools capturing mouse click telemetry can safely read evdev mouse events without exposing keyboard keyloggers to unprivileged user sessions.

Problem & Background

Under Wayland, compositors isolate window inputs so unprivileged applications cannot snoop on global desktop events. While the FreeDesktop ScreenCast portal shares screen content and cursor coordinates as frame metadata, it intentionally does not stream mouse button presses or keyboard events.

Applications like OpenScreen that animate mouse clicks or render ripple effects bypass this portal limitation by reading left mouse button presses (BTN_LEFT) directly from the kernel evdev interface (/dev/input/event*).

The Security Risk of the input Group

Because /dev/input/event* device nodes are owned by root:input, application installation guides commonly instruct users to add themselves to the group:

sudo usermod -aG input $USER

This presents a serious privilege leak:

Threat Profile Comparison

Capability Default Wayland With Mouse udev uaccess With Scoped setgid Binary With sudo usermod -aG input
Keystrokes / Passwords 🔒 Blocked 🔒 Blocked ⚠️ Exposes attack surface (helper has group input) 🚨 Exposed to all user apps
Global Mouse Clicks 🔒 Blocked ⚠️ Readable by user apps 🔒 Blocked (only helper reads it) ⚠️ Readable by user apps
Relative Mouse Motion 🔒 Blocked ⚠️ Readable by user apps 🔒 Blocked (only helper reads it) ⚠️ Readable by user apps
Synthetic Click Injection 🔒 Blocked 🔒 Blocked 🔒 Blocked 🔒 Blocked
Device Grab / Freeze 🔒 Blocked ⚠️ Possible via ioctl 🔒 Blocked ⚠️ Possible via ioctl

Architecture Decision: Targeted udev vs. Scoped setgid

When designing a secure solution, two architectures are possible:

  1. Targeted udev uaccess (Recommended): Grants dynamic ACLs on mouse event nodes exclusively to the active desktop session user.
  2. Scoped setgid Wrapper: Creates a privileged binary (e.g. owner = "root", group = "input", mode 2750) so only the helper binary runs with input privileges.

The targeted udev route is the definitive, recommended solution:

Implementation via Declarative udev Rule

In NixOS or udev rules configuration (/etc/udev/rules.d/70-mouse-uaccess.rules):

KERNEL=="event*", SUBSYSTEM=="input", ENV{ID_INPUT_MOUSE}=="1", ENV{ID_INPUT_KEYBOARD}!="1", TAG+="uaccess"

How It Works

  1. Strict Filtering: The rule checks ENV{ID_INPUT_MOUSE}=="1" and explicitly excludes any device acting as a keyboard (ENV{ID_INPUT_KEYBOARD}!="1").
  2. Dynamic ACL Assignment: When matching, udev attaches TAG+="uaccess". Systemd's systemd-logind builtin then grants read/write POSIX ACLs (setfacl) on those specific mouse character devices exclusively to the currently active desktop seat user.
  3. No Keyboards Exposed: Keyboards remain mode 0660 root:input with no user ACLs, preventing any unprivileged user process from sniffing keystrokes.
  4. No Group Management: The user does not need to be in the input group, and permissions automatically transfer across desktop login sessions without logouts or system reboots.

References