LWN.net Logo

Where are the non-root X servers?

By Jake Edge
September 8, 2010

One of the heralded features that was supposed to come with moving the graphics modesetting code into the kernel (i.e. kernel modesetting or KMS) was that it would—finally—allow systems to rid themselves of an enormous body of code running as root: the X server. KMS has made its way into distributions now, but for the most part there has been no switch to running the X server as a non-privileged user. Progress is being made, but there is another missing piece, at least for multi-user systems: some way for processes to enforce exclusive access to files they want to open.

One need only look at the recent kernel hole that was exposed by root-privileged X servers for a good reason to want an unprivileged X. It is a complicated chunk of code that is exposed to all manner of attacks, both local and, potentially, from across the network. It has been the source of vulnerabilities in the past and almost certainly will be again in the future. Reducing its privileges and possibly running it as a separate user will make any attacks against it less potent or completely ineffective—the recent exploit would have been stopped cold for example.

Prior to KMS, the X server had to do all manner of poking at the hardware to get its job done, and that required root privileges. Once that code moved into the kernel, the X server just needed to be able to access the devices provided. The graphics device driver enforces exclusive access so that other processes on the same machine cannot intercept—or interfere with—graphics commands, but there is another set of devices, /dev/input/*, that is more problematic.

In current systems, where X runs as root, it owns the files in /dev/input and the permissions only allow root to access them. If X were to run as either the logged-in user or some other separate user, overly restrictive permissions could not be used. For multi-user systems, regular users could end up with a "dangling" reference—in the form of an open file descriptor—to an input device. Once another user started X, that reference could be used for keystroke logging.

One possible solution would be to add a revoke() system call to Linux. That call would disconnect all processes from a file and allow the caller to have exclusive access. Unfortunately, no one has found an acceptable way to add revoke() capabilities to the kernel. There have been several attempts over the years (we most recently looked at one in 2007), but it is a hard problem to solve, mostly due to things like mmap()-ed files and the private copy-on-write mappings that are generated by fork().

With a working revoke(), the X server could just ensure that it is the only process that has access to the input stream. An alternative would be to have the X server run as a system user that lives in a specific group with access to the input devices, but that has flaws of its own. An exploit against the server would potentially give an attacker a means to access all users that are logged into X sessions, so a malicious local user or some remote exploit of a vulnerable X program might be able to affect all users of the system.

Keystroke logging can obviously lead to root compromise if someone types in the root password, but there are other things that users type that they don't want exposed, of course. Passwords for other systems (e.g. ssh, web applications) and all kinds of sensitive information (e.g. financial data for Gnucash or Kmoney) are input into X sessions. While it would be nice to get away from running X as root, the benefit needs to outweigh the cost—easy keystroke logging does not pass that bar.

The Moblin mobile distribution pioneered "non-root-X" and its descendant MeeGo has continued down that path, but neither of those distributions allows for multiple users. If there are no other users that could get access to the input devices, it is fairly straightforward to run the X server as the logged-in user, which is what Moblin/MeeGo do.

Ubuntu has been looking at the problem as well. There is a blueprint for the feature that is targeted for Ubuntu 10.10, but with a "Low" priority and it has not made an appearance in the recently released Beta. Unlike MeeGo, Ubuntu and other distributions will need to deal with multi-user use case, which seems to be the sticking point.

Fedora also recently discussed a non-root X server, after Mike McGrath asked about it on fedora-devel. That led to Matthew Garrett's security quote-of-the-week pointing out the problem with input devices and no revoke(). While some thought that was a good argument for PackageKit's ability to perform root-privileged actions without a password being typed in, Gregory Maxwell was quick to point out the flaw in that thinking:

This is an improvement because if Fedora removes "the need to ever type a root password" by simply allowing packagekit to give the user all the root abilities the user needs then the attacker doesn't need to wait around for the user to do something privileged, they can just ask packagekit as the user to do it for them. I'm sure this will save a lot of time.

So, at least for multi-user systems, we are still a ways out from seeing X servers running as a non-root user. The hardware access issues have been resolved—for those graphics cards that have KMS drivers—but there are still underlying plumbing issues that haven't been. For older hardware without KMS drivers, or those with proprietary-only drivers, X is always going to have to run as root.

It would be nice to limit the damage an exploit can do to only the user that got exploited, rather than the entire system or all logged-in X users. But that will require revoke() which doesn't seem to be in the pipeline. Conceptually, revoke() is a completely reasonable addition to the kernel, and it really isn't clear why we don't have it yet. It is certainly something that the security community could be working on to remove it as a barrier to a more secure X server.

Starting out by running X as a system user with various udev permission-switching rules and some kind of arbiter like ConsoleKit as Ubuntu is attempting might be the right approach. It definitely seems like Ubuntu has made the most visible progress toward the goal. Other distributions may be taking a wait-and-see approach in the interim.


(Log in to post comments)

Where are the non-root X servers?

Posted Sep 9, 2010 2:15 UTC (Thu) by ctpm (subscriber, #35884) [Link]

Thus says the article:

"An alternative would be to have the X server run as a system user that lives in a specific group with access to the input devices, but that has flaws of its own. An exploit against the server would potentially give an attacker a means to access all users that are logged into X sessions, so a malicious local user or some remote exploit of a vulnerable X program might be able to affect all users of the system."

Well OK, but isn't that also the case for the usual X server running as root?
So, while running X with a dedicated user/group is not perfect, it seems like an improvement in that it makes gaining root privs. more difficult. Why must this be an all or nothing affair? Switching to a dedicated user/group would be a nice step in the right direction and doesn't make things any worse, so distros should take it, even while they continue trying to figure out a better way.

Regards

Where are the non-root X servers?

Posted Sep 9, 2010 6:39 UTC (Thu) by drag (subscriber, #31333) [Link]

If your going to do it, you might as well do it right.

------------------------

There are 3 major situations you have to deal with.

1. Typical desktop --- Single user, just give the user's X Server rights to all inputs by default and everything is great.

2. Single seat, Multi User --- multiple users logged in, but only one is active at a time. You give only the active X Server rights to all inputs and deny it to everybody else. When they switch from one X Session to another the rights follow the active one.

3. Multi seat, Multi User --- Multiple users logged in, multiple sessions being used simultaneously. With this you depend on the administrator (privileged user) to manually delegate which X session gets which input.

-----------------------

Really the solution is just very obvious for all 3:

Your going to need a privileged "input proxy" that relays the input via sockets to the correct X Server. Then that proxy decides, probably (optionally) with assistance with packagekit and dbus, to give which user the inputs based on criteria like:
Is the X Session the current one in focus on the machine?
What X Session was given rights to what input by administrator?
I a user already using it?
etc etc.

To make it simple for administrators and users you just serve up input to whatever X session is active first. This is already what behavior people naturally expect anyways.

It's something that should be very lightweight. Just relay the raw input directly to the X Server and let the existing X Server code for auto detecting inputs and such take care of the details. You'd just have to change things around so that instead of reading from multiple file descriptors in /dev/input your reading from sockets in /var/something.

Where are the non-root X servers?

Posted Sep 9, 2010 6:41 UTC (Thu) by drag (subscriber, #31333) [Link]

> To make it simple for administrators and users you just serve up input to whatever X session is active first.

That is, I mean, by default. You'll have it configurable for multi-seat, multi-user setups.

Where are the non-root X servers?

Posted Sep 9, 2010 6:55 UTC (Thu) by rvfh (subscriber, #31018) [Link]

Are you not just going from 'one X server running as root' to 'one input server running as root'? It does mitigate the problem I suppose, and maybe it's the only way (apart from revoke()), but does it solve it?

Where are the non-root X servers?

Posted Sep 9, 2010 13:02 UTC (Thu) by foom (subscriber, #14868) [Link]

Yes. You have to privileged code handling input, either living in the kernel or in a privileged process. It's possible to write secure code, it's just very hard when you have as much code as the X server does (or...for that matter, the linux kernel, but that's a different issue). If you put the input server in a separate privileged process, you have to secure a lot less code, so the problem becomes feasible.

And note that it doesn't actually have to run as root, just as a priv user.

Where are the non-root X servers?

Posted Sep 9, 2010 23:45 UTC (Thu) by martinfick (subscriber, #4455) [Link]

Additionally, this code would not likely be exposed to nearly the huge attack base that the current X server is exposed to. It would likely only be exposed to the proxied data, and perhaps to some user switching input code, but that is surely a greatly reduced attack vector than the current model.

Where are the non-root X servers?

Posted Sep 13, 2010 21:30 UTC (Mon) by oak (guest, #2786) [Link]

And even that attack surface is reduced if that input daemon doesn't proxy the input, but only tells X which input device to use and on "hot seat" arrangements takes care of switching access rights on the input devices so that only the active X server can access given device at any given time...?

Where are the non-root X servers?

Posted Sep 9, 2010 12:43 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

Maybe we can create 'multiplexed' devices?

For example, we can use CUSE to create a personal set of devices for each user. When X is switched between users, we can connect/disconnect these devices from the real underlying input devices.

Where are the non-root X servers?

Posted Sep 9, 2010 18:27 UTC (Thu) by bronson (subscriber, #4806) [Link]

I was thinking along these lines too. LXC does a good job of giving individual processes their own custom views of system resources. I haven't used it for fine-grained access control yet but, from what I hear, it seems like it would do a good job of isolating X processes and global devices from one another.

Where are the non-root X servers?

Posted Sep 16, 2010 16:26 UTC (Thu) by Wol (guest, #4433) [Link]

I was thinking something like that.

We need a dedicated device driver, then some way of converting that to a file :-) So we can put the "revoke" type code in the device driver (or just above it in HAL or whatever). When a process "opens" the keyboard device, it gets a new file handle from the device driver, which disconnects and leaves any previous file handles hanging (or points them at /dev/zero :-)

Either way, unlike the screen device driver that enforces multiplexed access, the keyboard/mouse/etc drivers enforce simplex access.

Cheers,
Wol

Where are the non-root X servers?

Posted Sep 16, 2010 8:45 UTC (Thu) by NikLi (guest, #66938) [Link]

Right.

In my opinion, on a single-user system (99% of PCs today!), devices like /dev/input/mice and /dev/tty? should be readable by the user and /dev/fb? read/write by the user (as long as it's a local user). It doesn't make any sense to make those devices administrator-only.
(as a matter of fact, locally, I chmod them +rw by the init scripts in order to be able to run linuxfb applications as user)

It did made some sense in the old days because some students with telnet would do stuff like "echo "Teh Admin is BOFH" > /dev/tty0" and that would be printed on the admin's monitor while he was giving a presentation to the board and be very embarassed. But multiuser systems are more rare and their administrators have the knowledge to tune them.

Where are the non-root X servers?

Posted Sep 16, 2010 16:28 UTC (Thu) by Wol (guest, #4433) [Link]

Multi-user systems more rare? Not rare enough! My home system is multi-user - I've got a Phenom X3, and use my old pc as a terminal so both me and my wife can share the main system. I bet there are plenty of systems like that around.

Pretty easy even for a non-BOFH to do ...

Cheers,
Wol

Where are the non-root X servers?

Posted Sep 17, 2010 8:40 UTC (Fri) by NikLi (guest, #66938) [Link]

That is still single user. By "multiuser" i wasn't referring to multiple user processes at the same time but rather a system that gives (mostly remote) access to untrusted users.

In other words, i believe that the user sitting on the desktop should be -by definition- priviledged to use the input/framebuffer devices.

Where are the non-root X servers?

Posted Sep 9, 2010 9:22 UTC (Thu) by xav (subscriber, #18536) [Link]

One problem here is that those mmap problems are a non-issue for a revoke() working on /dev/input/*.
So why not juste making a simple revoke() syscall first, which works for X, and then extend it to work for everything later ?

Where are the non-root X servers?

Posted Sep 9, 2010 2:44 UTC (Thu) by airlied (subscriber, #9104) [Link]

Revoke also brings it own set of issues.

Since X isn't the only process listening to input devices, stuff like rfkill need to access the hw, and really don't want to be revoked, again you'd rather those apps didn't run as root either, but revoking would kill them all as well. Probably need some sort of capability for those daemons to allow them to avoid revoke, and then an evdev specific revoke ioctl could be used.

Where are the non-root X servers?

Posted Sep 9, 2010 15:20 UTC (Thu) by dgm (subscriber, #49227) [Link]

Isn't rfkill a kernel subsystem? I read that it *provides* switch state information, as opposed to consuming it. Is this right?

Where are the non-root X servers?

Posted Sep 9, 2010 4:10 UTC (Thu) by jmspeex (subscriber, #51639) [Link]

Just curious, why not have a (presumely small) "input server" that would run as root instead of having the whole X server run as root? That server would just serve as an interface to the input devices and implement the equivalent of revoke(), but from userspace.

Where are the non-root X servers?

Posted Sep 9, 2010 5:22 UTC (Thu) by butlerm (subscriber, #13312) [Link]

That was my first thought too. If it is good enough for SSH...

Where are the non-root X servers?

Posted Sep 9, 2010 8:38 UTC (Thu) by michaeljt (subscriber, #39183) [Link]

> Just curious, why not have a (presumely small) "input server" that would run as root instead of having the whole X server run as root? That server would just serve as an interface to the input devices and implement the equivalent of revoke(), but from userspace.

Perhaps s/root/a privileged but non-root user/ ? And I'm not sure that revoke() would be necessary here, it might be enough just to only allow one process access at a time, and to rely on that process to voluntarily relinquish access. After all, that is what is happening for the graphics part.

This might also be another step along the Wayland path to where the X server is a simple user-space application and the input and graphics hardware is managed elsewhere.

Where are the non-root X servers?

Posted Sep 9, 2010 15:15 UTC (Thu) by Tobu (subscriber, #24111) [Link]

That plus the ConsoleKit integration drag suggests seems to be a winner.

Where are the non-root X servers?

Posted Sep 10, 2010 0:14 UTC (Fri) by mezcalero (subscriber, #45103) [Link]

Well, this solution might be working for input, but is not enough for stuff that is more complex, i.e. the dri device nodes, or even in a very similar case the audio devices for PulseAudio. They require complex ioctls, mmapping and similar, and adding the extra latency by bouncing everything of a multiplexer daemon won't do much good here either.

In PA when you switch sessions the device is currently handed over cooperatively. I am eagerly waiting for the day we can invoke revoke() for audio devices to do this properly.

revoke

Posted Sep 9, 2010 10:37 UTC (Thu) by djm (subscriber, #11651) [Link]

Why is revoke(2) so hard? It has been in BSD since 4.3...

revoke

Posted Sep 9, 2010 13:05 UTC (Thu) by ebiederm (subscriber, #35028) [Link]

And revoke(2) only works on a small set of devices. It is the general case that is tricky.

revoke

Posted Sep 9, 2010 15:01 UTC (Thu) by Tobu (subscriber, #24111) [Link]

X isn't the general case, revoke() on a small set of devices is exactly what it needs.

Kernel interfaces should be introduced for a purpose. The trickier interface for mmapped files is a pointless distraction; there is a risk getting it wrong, a cost in maintaining it, and no obvious benefit in supporting it.

revoke

Posted Sep 9, 2010 18:50 UTC (Thu) by ebiederm (subscriber, #35028) [Link]

In the special case mmap isn't a problem either, it is already solved for all sysfs files.

I am slowly working the on what is essentially revoke functionality from the direction of hardware hotplug. If someone working on the input subsystem wants to contact with me so we can conspire together to fix this I would be more than happy to work with them.

Is it done on other Unices?

Posted Sep 9, 2010 21:57 UTC (Thu) by giggls (subscriber, #48434) [Link]

I'm not quite certain about it, but I thought that X is not running as root on Solaris.

Did any other proprietary Unix Version have an X Server not running as root?

Is it done on other Unices?

Posted Sep 12, 2010 0:20 UTC (Sun) by SkyGuy (subscriber, #60773) [Link]

I have a Sun Blade 100 running Solaris 10, its X server is running as root, heck even dtgreet is running as root.

Hmm, how does XDM handle it currently?

Posted Sep 10, 2010 0:02 UTC (Fri) by martinfick (subscriber, #4455) [Link]

I was just wondering what happens when the local X server connects to a remote XDM session. Isn't it possible for a user that doesn't exist on the local machine to log into a remote machine? Who then does the local X server run as once logged into the remote? If it is the same user (root) as before the login, couldn't that user simply not be root to start with? Would simply treating all logins as remote logins solve this easily?

Where are the non-root X servers?

Posted Sep 29, 2010 11:29 UTC (Wed) by gdamjan (subscriber, #33634) [Link]

is there anything I can do *today* to configure a non-root X server on my laptop? This laptop is a single-seat/single-user all the time.

Copyright © 2010, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds