login: handle -EALREADY from bus_verify_polkit_async_full()
Commit
536c18e5c3 ("bus-polkit: shortcut auth. after first denial")
added logic to async_polkit_query_check_action() that returns
-EALREADY when a failure or denial decision was made for a previous
action.
This has the consequence that root is able to ignore inhibitors and
shutdown etc. even when polkit explicitly denies it. This is because
when systemctl's verb_start_special() calls logind_reboot(), unless
the call succeeds or returns one of -EACCES, -EOPNOTSUPP, or
-EINPROGRESS, a fallback path is taken to attempt the action without
going through logind. Hence, since logind_reboot() started returning
-EALREADY in some cases, the fallback path was taken, and the shutdown
was performed anyways.
For example:
root@ubuntu:/# cat /etc/polkit-1/rules.d/10-systemd-logind-no-skip-inhibitors.rules
// Never allow strong inhibitors to be ignored.
polkit.addRule(function(action, subject) {
if ((action.id == "org.freedesktop.login1.power-off-ignore-inhibit" ||
action.id == "org.freedesktop.login1.reboot-ignore-inhibit" ||
action.id == "org.freedesktop.login1.halt-ignore-inhibit" ||
action.id == "org.freedesktop.login1.suspend-ignore-inhibit" ||
action.id == "org.freedesktop.login1.hibernate-ignore-inhibit")) {
return polkit.Result.NO;
}
});
root@ubuntu:/# systemctl reboot -i
Call to Reboot failed: Operation already in progress
..but the reboot continues anyways due to the fallback.
To fix this, add logic in systemd-logind's verify_shutdown_creds() to
handle -EALREADY from bus_verify_polkit_async_full(): if we receive
-EALREADY when checking authorization for <action>-multiple-sessions,
and we are blocked on inhibitors, continue on to get the decision for
<action>-ignore-inhibit directly.
While here, add similar logic to method_inhibit(), which may need to
verify multiple polkit actions in a single call.
Fixes
536c18e5c33fd682fcd38d228b46a339adbe150b