Had a conversation about how this could work with Patrick, this is my current set of ideas/plans after that conversation.
We have the emerg-shutdown
background process now, which among other things monitors all keystrokes on the system by grabbing the /dev/input/event* devices on startup. This is done so that if the user presses a panic key combination (user configurable, the default is yet to be determined, see Emergency key press shutdown sequence), the system can be immediately and forcibly shut down, without any possibility of malicious interference or accidental overriding. That same mechanism could be used in theory to listen for any key combination though, allowing us to define keyboard shortcuts that talk to a privileged component of the system (similar to the Secure Attention Key in other operating systems). Another advantage of this is that it’s not possible for malware to access this interface unless it is running as root, because emulated input mechanisms allow communicating with the display server, not the kernel’s evdev driver. Injecting key events into the kernel’s evdev subsystem requires using uinput, which requires root privileges to access, thus unprivileged malware can’t send keystroke combinations in order to trigger whatever privileged actions are configured here.
Since shortcuts configured in emerg-shutdown require either root privileges or physical access to the machine to trigger, this makes it seem like the perfect place to implement a Ctrl+Alt+Delete handler. The question then becomes, what should this handler do?
There are two possible directions we could take this. One is the simple route - register the salute in emerg-shutdown, and send a DBus message to whatever user session is on an active TTY in response. Those messages would then instruct an agent running as the user to do something like start a task manager or lock the screen. This would be useful, but it would only be marginally better than just defining a keyboard shortcut in the user session. It wouldn’t allow the user to kill applications that are running as root or as a different user (something they may have a legitimate reason to do even in a user session), and it wouldn’t prevent anything “going wrong” in the user session (a malicious or runaway process, for instance) from being able to interfere with the salute’s functionality in some way.
The other, more complex route, would be to switch to an “emergency session” of sorts, moving the user to an unused TTY and launching a graphical session in that TTY where they can do things as root or another privileged user. This is far more powerful, and possibly far more secure if done right.
The more complex solution is unsafe under X11 because of the world-writable UNIX sockets created by X11, exposing attack surface that could result in exploitable escalation of privilege vulnerabilities. Under Wayland however, the display server’s socket is generally under /run/user/UID, which has strict permissions to prevent access by other users, so as long as the kernel’s permission subsystem holds up, this shouldn’t be an issue. We can thus run a separate Wayland server as a privileged user (such as sysmaint
) alongside an unprivileged and untrusted user session, without as much fear of vulnerabilities. (If running a full Wayland server sounds like too much risk, we could also use a raw framebuffer. Qt supports rendering directly to a framebuffer, it’s very slow at least under a virtual machine, but it works. I think this is overkill though, and it will also make it hard to work with multiple programs at once, which would almost certainly be desirable.)
One danger of an emergency UI such as this is that it’s not impossible for a malicious application to prevent the user from escaping to the emergency session. The emergency session would simply run in a TTY (since that’s the only feature Linux has for this sort of thing), and it would be fairly trivial for malware running as a user in a graphical session to simply force the user back to the original TTY, using a console file descriptor. (One can switch the active TTY to any other TTY if they have write access to any one TTY. In other words, if the user is logged in graphically and thus /dev/tty7 is writable by them, malware running as that user can open /dev/tty7 and then switch the user to any desired TTY.) To prevent this, one solution would be to SIGSTOP potentially malicious processes, specifically those which run as a logged-in user. This cannot be done to every process in the system though, since:
- PID 1 cannot be SIGSTOP’d (I tried, the signal was silently ignored)
- A SIGSTOP’d program can be SIGKILL’d without receiving a SIGCONT first
- systemd’s watchdog mechanism can and probably will restart a service that is SIGSTOP’d as a result
However, “freezing” all logged-in user accounts should still be feasible in theory. This would prevent any running malware from doing anything while the user is in the emergency session, thus it would be unable to force the user to a different TTY. Any malware on the system that wasn’t running as a logged-in user would have to be running as root or be able to elevate its privileges to that of a logged-in user in order to gain access to a console and thus change TTYs.
One thing that is still uncertain about SIGSTOP is how it behaves when dealing with stuck kernel threads. If a thread calls into the kernel and then gets stuck there (such as can happen when dealing with flaky I/O devices), it cannot be SIGKILL’d or SIGSTOP’d. What’s unclear is whether the process’s other threads can be SIGKILL’d or SIGSTOP’d in this situation. If the answer is “yes”, then using SIGSTOP should be an effective freezing mechanism, but if the answer is “no”, then malware may be able to circumvent a SIGSTOP by launching a thread that then does something to get stuck in the kernel. Getting a thread stuck in kernel mode is hard though (I believe it should be impossible without either buggy hardware, a buggy kernel, or root access, and even with root access you’d have to do something convoluted like set up a VM with an NFS share, mount the NFS share on the host, kill the VM abruptly, and then attempt I/O on the mounted share edit: just tried something like this, even this doesn’t result in a stuck kernel thread, Creating controllable D state (uninterruptible sleep) processes has better instructions for getting a process stuck in a kernel thread), so this might not be an issue.
Another difficulty is that we most likely are not going to go through a display manager., As a result, the “session” we will end up in will be rather limited and things that one might expect to work (like systemd user units, DBus, some xdg-related things, etc.) probably won’t work. Thus the functionality within the emergency session will need to be limited to cope with this. (This will also prevent us from simply switching to a sysmaint session when the salute is registered - the sysmaint session has features that depend on a full login session running. It most likely will not behave itself well if it doesn’t have all of the infrastructure usually set up by a display manager working right. We also can’t just switch to the sysmaint session because some of the features of the sysmaint session, like software updates, are not things that can be safely done when booted into a user session. Even if they work now, they won’t work once we implement Verified Boot.)
Obviously, some form of authorization will be needed in order to access the features of this emergency session. Otherwise any attacker with physical access to the system could use this to take the system over entirely. It shouldn’t be too difficult to implement an authorization prompt of some sort to make this work. Because of the need for authorization, the graphical session MUST NOT run as root, since then any arbitrary code execution vulnerability in a used graphical toolkit or Wayland compositor could allow an attacker with physical access to gain unauthorized root access. The sysmaint account would be ideal to use instead, since then if the user has set a password, they will have to provide it in order to run any privileged operations.