The 14.04 release of Unity unfortunately shipped with a few security vulnerabilities in the newly introduced screenlocker. As we will also ship a reworked screenlocker in Plasma Next I started to do another code audit, add more unit tests and try to make the code easier to understand and maintain. Furthermore I think it’s a good reason to explain how screenlockers work in general on X11 (and why it is easy to introduce security vulnerabilities) and the screenlocker in Plasma Next in particular. To make one thing clear: this post is not meant to shame Ubuntu for the issues. Some of these whoopies would have been possible in Plasma, too, and that’s the reason why I looked at the code again in more detail. On the other hand I think that our screenlocker in Plasma Next could be a solution for Unity’s use cases and I would appreciate if Ubuntu would adpot our solution.
What a screenlocker should protect
Before looking at how it works in X11, I shortly want to discuss what a screenlocker can protect and what is out of scope for a screenlocker. In general there are two tasks for a screenlocker:
- Blocking input devices, so that an attacker cannot interact with the running session
- Blanking the screen to prevent private information being leaked
The lock is hold till an authorization is provided (e.g. password). I’ll discuss in more detail what these authorizations are in the case of Plasma.
There are a few things the screenlocker cannot protect against:
- Evil software already running under the user’s account
- Attackers having access to a tty running under the same user
To explain the first problem we must remember that X11 is very bad from a security point of view. This adds a general limitation to what can be protected. Getting a key logger with X11 is extremely trivial, there are easier ways to get the password than to try to attack the locker.
The second problem goes in the same direction: if someone has access to a tty, he can install an X11 key logger. It will not get the password, but still it’s a huge problem. Also the attacker could reconfigure the locker or run a debugger on the lock process to just end the locker. Some of these problems can be mitigated by the distribution through hardening (e.g. SELinux).
I plan to harden the system against these two problems in a future release. Though given the general problems of X11 it will never be possible to completely solve these two problems on X11.
How screenlockers work in X11
X11 doesn’t really know the concept of a screenlocker. Thus one needs to use the concepts of X11 to try to get something like a screenlocker. The main features are grabbing the keyboard and grabbing the pointer. What’s important to know about these two X11 features is that only one X Client is allowed to grab the keyboard and pointer. If the keyboard or pointer is already grabbed the lock cannot be established. For example if a (context) menu is open the screen cannot be locked or if Present Windows is active.
Having the keyboard and pointer grabbed are essential to fulfill the first task of the screenlocker. This also shows why it’s so difficult to get a locker right. As soon as the process grabbing the keyboard goes away the lock is released. As soon as that happens the screen is unlocked. Thus there is an easy scenario to attack a screenlocker: get the lock process to crash and restart and try to grab the keyboard before the screenlocker is able to re-install the grab.
This is the problem Unity run in and to be honest we also almost run into this issue in the past. Thus a screenlocker implementation must be able to hold the keyboard grab during a crash. A lock file might not be sufficient. I will explain later how we made the screenlocker in Plasma crash resistant, so that the session will never be unlocked.
The second task is blanking the screen. This is done by creating a lock window and raise it on top of all windows and ensure that the lock window is always on top of all windows. Given the description one can notice that this is kind of a fragile process. Assume one has two processes trying to be on top of each other: they will constantly try to raise on top of the other. Something I once experienced by having the screen locked during session startup resulting in a race between login and lock screen. Again Wayland will improve the situation as the compositor (in our case KWin) will be aware of the screenlocker and will ensure the restrictions.
Screenlocker architecture in Plasma Next
The architecture for locking screens got simplified in Plasma Next. Support for screen savers got completely removed (fear not: we have plans for replacement, probably not in Plasma Next, but in a future release) and that helped a lot to trim down the architecture.
In Plasma Next the locking consists of three interacting processes:
- KSLD in KSMServer
- kscreenlocker_greet (greeter)
While this might sound complex to have three processes it’s following the unix philosophy of one tool, one task. So let’s look at what the processes do.
KSLD (for KScreenLocker Daemon) is a library used by KSMServer. KSMServer is our session server which gets started during session startup and keeps the session alive. If it crashes the session dies with it and you are returned to the login screen. KSLD is the main component of the screenlocker: it listens to the activation signal (e.g. global shortcut or logind), installs the X11 lock, blanks the screen and waits for the authorization to unlock the screen again.
At the moment KSLD has three authorization mechanisms:
- User configurable grace time: any user input during the grace time interval immediately unlocks the screen
- logind: listens to Unlock signal on the session’s object
- greeter process: exits with exit code 0
The most complex authorization mechanism is the greeter. For that the greeter process needs to be started and is monitored by KSLD. If the greeter process crashes the lock is kept and the greeter process is started again. The authorization to unlock is provided by the exit code of the process. If it exits with 0 the screen gets unlocked, with any other code the greeter gets started again.
Of course we need to show the greeter – so far the screen is blanked and no window is allowed to go above the blanked window. To achieve this the greeter can set a special window property on its windows and ksld will raise those above the locker and forward input events to this X client.
Security note: this cannot protect against malicious software already running on the user’s session. A malicious software could set the same property and thus read the input events. I intend to change this for a future release to have a socket communication between ksld and the greeter to pass window information and input events. Obviously it’s a good idea to use the Wayland protocol for this task – also on X11.
The second process in our architecture is kscreenlocker_greet which got partially already explained in the previous section. In Plasma it’s responsible for rendering the unlock screen interface and the session switching interface. The user interface is implemented using QML allowing us to easily adopt to new use cases – e.g. the unlock screen in Plasma Active is just a different QML package.
For future releases we have some ideas to improve the experience by allowing to run our normal wallpaper plugins (bringing back animations) and allowing some selected plasmoids to go on the lock screen. Of course we need to white list the plasmoids which are allowed to go on the lock screen to not expose private information or even allow to start processes. It would be a bad idea to add a terminal emulator as a plasmoid.
As mentioned our architecture is following the “do one thing, do it right” approach and the task of the greeter is only to provide the user interface. It does not perform any password verification. For that it uses kcheckpass, which is a small terminal application to verify a provided user password. If the user clicks the unlock in the greeter kcheckpass is invoked and the password is passed to kcheckpass through a binary socket protocol. Kcheckpass communicates with PAM to verify the password and can pass back an authentication success, authentication error or further information for PAM modules needing further interaction like a fingerprint reader. These messages and authentication failure are passed back to the greeter ui. If the authentication is successful the greeter exits with exit code 0.
This concludes the look on the various components. I would encourage all readers to try to break the architecture before we have a release. I’m quite confident that the architecture is secure and also more secure than what we had in 4.x.
Last but not least I want to share some thoughts on XScreenSaver. Whenever an issue arises like the ones in Unity we can hear people claiming one should use XScreenSaver because it’s way more secure. Now personally I don’t believe in silver bullets – especially not if it comes to security. In the beginning of this long blog post I stated two tasks a screenlocker needs to provide and XScreenSaver can fail with the second: the screen content can be exposed. Consider for example the linked screenshot. For a screen saver of the last millennium this was a suitable solution, but not for an implementation where we want to focus on security.
As we can read in the section On Toolkit Dialogs the security of XScreenSaver is based on the fact of not using a GUI toolkit. While the argumentation sounds good, it fails to see the need of today’s desktop environments. Providing an unlock dialog which can be styled with XLib might be a solution for the 90ies but not in a world where we want to provide an awesome user experience. Today users also expect the current time, battery state and many more information on the lock screen. We need to provide accessibility features which is not possible in XScreenSaver. We need to use proper idle mechanism shared with other applications. We need DBus integration, inhibition (XScreenSaver doesn’t support this!) and logind integration. Not using toolkits also means to have to fix bugs which the toolkit would solve for you, this is again increasing the risk of broken code and crashes in the daemon. Just consider this piece of code I found in XScreenSaver:
/* It turns out that if we do setlocale (LC_ALL, "") here, people running in Japanese locales get font craziness on the password dialog, presumably because it is displaying Japanese characters in a non-Japanese font. However, if we don't call setlocale() at all, then XLookupString() never returns multi-byte UTF-8 characters when people type non-Latin1 characters on the keyboard. The current theory (and at this point, I'm really guessing!) is that using LC_CTYPE instead of LC_ALL will make XLookupString() behave usefully, without having the side-effect of screwing up the fonts on the unlock dialog. See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923 from comment #20 onward. -- jwz, 24-Sep-2011 */
Now if you consider the architecture I explained the “problem” of the toolkit doesn’t matter at all. We are running an interpreted (QML) language which heavily uses OpenGL without introducing any security risks by using separate processes. If the application which holds the lock (and also heavily uses Qt) crashes the complete session goes down. In turn I think that the argumentation provided by the XScreenSaver maintainer doesn’t hold. Even more I haven’t seen anything about crash resistance like we have in Plasma to have the session getting killed if the lock process crashes. I’m not saying that this doesn’t exist (the code base is very large and I haven’t done a full audit) but I haven’t seen anything in the very prominent documentation about why it’s better.
Of course one could ask why we don’t work on XScreenSaver to improve it and make it work for our use cases. I don’t think that this is possible and at least I would not even try to. It’s quite clear that code using toolkits is not wanted. I also doubt that the needs of desktop environments like Plasma, GNOME Shell or Unity are understood at all. Let me quote the FAQ:
KDE suffers from the same brain damage as GNOME, above.
The only sensible (and secure) way to use a screen saver under KDE is to turn off KDE’s built-in screen saver, and use xscreensaver instead.
With that attitude I think it’s quite obvious why at least I see zero chances to use XScreenSaver in any desktop environment.