Re: [GIT PULL] kdbus for 4.1-rc1

Tom Gundersen
2015-04-16T12:02:49Z

On Wed, Apr 15, 2015 at 2:09 PM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Wed, 15 Apr 2015, Greg Kroah-Hartman wrote:
>
>> 'systemctl reboot' calls a bunch of other things to determine if you
>> have local access to the machine, or permissions to reboot the machine
>> (i.e. CAP_SYS_BOOT), and other things that polkit might allow you to do,
>> and then, it decides to reboot or not. That happens today, right? I
>> don't understand the argument here.
>
> And what exactly is the argument that this is the way it should be
> implemnted?
>
> Why can't it just rely on the kernel to provide final answer to "to reboot
> or not to reboot, that is the question"?
>
> At the end of the day, it's the kernel that decides whether it will really
> ultimately ask the platform to reboot.
>
> If, for whatever reason (which might be completely invisible to userspace)
> kernel decides not to do so, userspace has to be able to recover from such
> failure in any case.

This is not how shutting down a general purpose operating system
works. If a system is shut down, all user sessions are terminated, all
services are stopped in the right order, all remaining processes
killed, all file systems are unmounted, all storage devices
disassembled, and so on. All this is implemented entirely in userspace
and involves a number of complex transitions from the normal init
system, to a shutdown PID 1 process and finally a transition back to
the initial ramdisk so that we can unmount the root file system even.
After all that is done, in the right order, following dependencies,
while enforcing timeouts, then the very last step is actually the
reboot() system call that then brings the kernel to a halt, and
possibly turns off power.

Thus I don't see how your suggestion can be applied in any way to how
system shutdown works: the shutdown procedure includes these
non-trivial preparation steps described above, and it is essential
that this preparation is not begun unless the client requesting it
actually has sufficient rights to do so. Or to put this another way:
if the system went all the way down, so that everything is killed,
unmounted, disassembled, to the point even that we transitioned away
from the root file system, then the reboot() system call is really
just the tiniest bit of it. And you should not be able to get there if
you originally didn't even possess the capability to execute that last
step...

Moreover, the daemon performing the shutdown tasks is necessarily
always privileged enough to do so, so calling into the kernel and see
what happens is completely the wrong thing to do (it would simply
succeed). What matters is if the client calling the daemon is
sufficiently privileged. If the client has the capabilites necessary
to call the reboot syscall directly, it makes no sense to disallow
them from doing a clean reboot. It would be like giving someone access
to pull the power plug, but not allow them to shutdown the machine
cleanly.

To conclude, the kernel makes the decision for allowing reboot() to
succeed based on CAP_SYS_BOOT, so when we decide whether or not to
perform the preparation steps, we really must also use CAP_SYS_BOOT.
If we are more restrictive, it does not gain us anything as people
with CAP_SYS_BOOT can just circumvent our logic and "pull the plug" by
calling reboot() directly. If we are less restrictive and for instance
check for uid==0 it would essentially mean that we have added a way to
circumvent the dropping of CAP_SYS_BOOT.

Cheers,

Tom