Fedora and pkexec
The nasty vulnerability in pkexec has been rippling through the Linux world, leading to lots of security updates to the underlying polkit authorization toolkit. It also led to a recent discussion on the Fedora devel mailing list about whether pkexec, which runs a program as another user, is actually needed—or wanted—in some or all of the distribution's editions. But pkexec is used by quite a few different Fedora components, particularly in desktop-oriented editions, and it could perhaps be a better choice than the alternatives for running programs with the privileges of another user.
Adam Williamson raised the issue on the day after the disclosure of the PwnKit flaw. It is, as he noted, a longstanding (since the first version of pkexec in 2009) local root privilege escalation. That started him thinking:
The issue and some of the comments around it prompted me to wonder - why is `pkexec` still a thing? Particularly, why is it still a thing we are shipping by default in just about every Fedora install?My best recollection is that pkexec was kinda a kludge to allow us to get rid of consolehelper: some apps weren't getting rewritten to the Right Way of doing things under policykit, they still just wanted to have the entire app run as root, and pkexec was a way to make that happen.
consolehelper is another program that allows users to run code as other users, while PolicyKit is a former name for polkit. Back in 2013, the Fedora Usermode Migration feature targeted moving all users of consolehelper to polkit. Williamson wondered which parts of Fedora still needed pkexec at this point (given its "kludge" status). But, since it is included in the polkit package, which is used far more widely, perhaps pkexec could be split out into a sub-package that would only be installed where that piece is actually needed, he suggested.
In a followup
message, Williamson noted that the bug ticket
for the Usermode Migration showed that it had not been completed. There
are still 15 packages in Fedora that require consolehelper. Peter Robinson said
that most of those "appear to be legacy
" packages that should
not be in general use.
Michael J Gruber jokingly asked if he should switch his package away from pkexec and back to using the consolehelper-based beesu wrapper for the su command since beesu still exists in Fedora. But Williamson said that is not his point at all:
The issue is not that pkexec is inherently worse than any other tool to do approximately the same thing (prompt for some kind of password, then run the entire app as root) - it's unfortunate that pkexec happened to have a giant security flaw, but it's not unlikely that other tools to do the same thing will turn out to have security flaws if someone decides to take a close look at them. The issue is that *that whole design* is suboptimal.
The idea was that applications would move to using the other mechanisms
provided by polkit in order to do their privileged operations, he said.
For applications that will not make that transition, pkexec was a
"less-good second choice option
" to allow them to continue to
work. Removing consolehelper was meant to reduce the number of
"run things as root" tools that the distribution needed to pay attention
to, though that has not ever fully been completed. "Switching from
pkexec to any other 'run-this-thing-as-root' tool
would not be an improvement.
"
If you are going to run programs as root anyway, though, pkexec is probably better than using sudo or other options, Lennart Poettering said. pkexec already uses the polkit interprocess communication (IPC) mechanism to request running a binary as root, which is a superior model in his mind:
I mean, polkit has some issues, but I am pretty sure that "pkexec" is not what I'd consider the big problem with it. Or to say this differently: the whole concept of tools like su/sudo/setpriv/runuser/suid binaries is [questionable]: i.e. I am pretty sure we'd be better off if we would systematically prohibit acquiring privs through execve(), and instead focus on delegating privileged operations to IPC services — but of course that would be quite a departure from traditional UNIX.[...] "pkexec" is a *short* program, it runs very little code with privileges actually. That makes it a *ton* better than the [humongous] code monster that "sudo" is. It has a smaller security footprint, and is easier to review than "sudo". That's worth a lot actually.
But Sam Varshavchik objected to the idea that moving the privileged operation to the other end of an IPC mechanism, such as a socket, really would solve the underlying problem. Having programs that run as root, which have bugs, is actually the problem at hand:
The same bug that's exploitable in a suid [or setuid] binary will also be exploitable, exactly the same way, in its suid-less equivalent. If you have a buffer overrun in a suid binary as a result of carefully-crafted command-line parameters or environment, then if you replace the suid binary with an identical bug-for-bug implementation that uses a socket to carefully pass along the same environment or parameters to a native root binary, and the buffer overrun is the same, guess what: you still have the same exploit.suid is not the problem. An execved program will inherit the environment, some open file descriptors, and maybe a few other things that a standalone daemon that accepted a socket connection does not have. But that's what most exploits leverage, so cleaning up the environment and open file descriptors would remedy that. It will take some effort to exploit whatever remains.
Poettering did not see things that way, however. There is an enormous amount of state that comes along for the ride when executing a setuid binary, and that state is growing over time:
The thing with setuid/setgid is that the invoked privileged process inherits a lot of implicit state and context that people aren't really aware of or fully understand. i.e. it's not just env vars and argv[], it's cgroup memberships, audit fields, security contexts, open fds, child pids, parent pids, cpu masks, IO/CPU scheduling priorities, various prctl() settings, tty control, signal masks + handlers, … and so on. And it's not even clear what gets inherited as many of these process properties are added all the time.If you do privileged execution of specific operations via IPC you get the guarantee that whatever is done, is done from a well-defined, pristine execution environment, without leaking context implicitly. The IPC message is the *full* vulnerable surface, and that's as minimal as it can get. And that's great.
While all of that context and state is present for the setuid program, the underlying problem is in the program itself, Varshavchik said:
And none of that makes any difference, whatsoever, unless there's also an underlying logical bug in the code itself. And that's going to be the real problem. So then, question becomes, how many exploits leveraged any of these exotic resources, like cgroup members, tty control, and all of those other fringe attributes? I suppose there might've been a few, but I can't recall many. On the other hand, exploits that accomplish execve("/bin/bash") would work equally well, given the same underlying bug in the root daemon, that's triggerable via its front end, or in a suid program. A sanitized environment won't be much of a help.
But sanitizing the environment fully is difficult or impossible to do for the setuid program, Poettering said. There are a number of interacting attributes that are under the control of the caller (read attacker) of the setuid binary—and that number keeps increasing:
You might *hope* that there was no bug introduced through the interaction of all that code, but the point I am making is that "struct task" and associated objects in kernel get extended all the time, and suid programs that didn't expect these additions will be exposed to them, and cannot sanely reset them even if they wanted, since typically developers cannot look into the future (and the cgroup stuff cannot even be reset reasonably at all).
Varshavchik wanted
"hard data
" on the kinds of vulnerabilities being envisioned
here. He and Poettering went back and forth about what constituted said
data, without coming to any real agreement. Poettering ended his
participation in that sub-thread by noting that he understands "your
love [for] suid and sudo
" but that he and others consider them to be
"a very bad
idea
". For his part, Varshavchik is not
particularly enamored with those mechanisms:
I love suid and sudo no more, no less, and exactly the same as I love my screwdriver. I'm married to neither. Both are just tools. Both can be used, by people, correctly. Both can be used also, by people, incorrectly. But it makes no sense to use that as a reason for replacing screwdrivers with wrenches.
Demi Marie Obenour suggested
that the state information that gets inherited by a setuid binary should be
chosen by that binary: "State inheritance definitely should be opt-in, with the possible
exception of certain audit data such as the audit user ID.
" Steve
Grubb, who works on the Linux audit
system, said that auditing relies on binaries inheriting security
contexts and audit information, so the existing polkit mechanisms fall
short. Had a kernel mechanism, like kdbus, been adopted, that problem would
have been
avoided. As it stands, polkit "doesn't satisfy our security
requirements
":
[...] it's impossible to reason about what's allowed and what's not because the policy is free-form javascript rather than assignments that can be checked by any configuration scanners. And access decisions do not go through the audit system. So, we really have little insight into access control and information flows from anything using IPC started applications.Setuid may be distasteful to some people. But it's properties are well known and its fully plumbed to make some assurance arguments about the platform.
He was not alone in complaining about polkit's JavaScript configuration mechanism, as that is seen as something of poor design decision by many. Meanwhile, Andrew Lutomirski said that getting an in-kernel IPC mechanism is a difficult problem; he had some thoughts on setuid as well:
I would love to see a nice security model for IPC in the Linux kernel, but I haven't seen a credible proposal. This problem is *hard*.As for setuid, I'm strongly of the opinion that setuid was and remains a mistake. Executing a process should never add privileges.
Circling back to his original message, Williamson noted
that the thread made it clear that pkexec is currently needed by
at least some of the desktop editions/spins of Fedora, though: "It's not an
easy question to answer since our
packaging doesn't distinguish between something needing *polkit* and
something needing *pkexec*.
" Fedora project leader Matthew Miller
thought
that at least could be changed: "I wonder if a first step might be
to split out pkexec into a subpackage, and
then gradually remove requirements on it.
"
That's where things stand at this point. Pulling pkexec out into its own package seems like a good plan, regardless of whether it should slowly be removed from use or not; it will make tracking which other packages use it that much easier should another vulnerability arise. A longer-term shift toward more—or exclusively—polkit-style privilege enhancement may not necessarily be in the offing. The consensus on that path is not entirely clear and it is also an area where the distribution does not want to see a lot of churn—at least without a lot of code review to go with it.
As with most (all?) vulnerabilities, the real culprit is the lack of focused security review of code. For setuid binaries and other code that runs as root, that review is imperative, but it is far from clear that a huge amount of it is being done. Every time another widespread vulnerability crops up we get reminded of that, but the impact seems to fade rather quickly and things return to "normal". That is a rather worrisome state of affairs.
| Index entries for this article | |
|---|---|
| Security | Distribution security |
