logind: refuse operations if the target unit is masked or unavailable

If hibernate.target is masked, and systemctl hibernate is invoked, havoc ensues.
logind starts the hibernation operation, but then doesn't go through with it;
gnome-shell segfaults. Let's be nice to the user and refuse doing anything in
that case.

$ sudo systemctl mask hibernate.target
$ busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanHibernate
s "no"
$ sudo systemctl hibernate
Failed to hibernate system via logind: Access denied
Failed to start hibernate.target: Unit hibernate.target is masked.

https://bugzilla.redhat.com/show_bug.cgi?id=1468003#c4
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2018-04-10 21:31:59 +02:00
parent bd062910c8
commit c8c8ee85af
3 changed files with 56 additions and 14 deletions
+23 -14
View File
@@ -33,6 +33,24 @@
#include "terminal-util.h"
#include "user-util.h"
const char* manager_target_for_action(HandleAction handle) {
static const char * const target_table[_HANDLE_ACTION_MAX] = {
[HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
[HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
[HANDLE_HALT] = SPECIAL_HALT_TARGET,
[HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
[HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
[HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
[HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
[HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
};
assert(handle >= 0);
if (handle < (ssize_t) ELEMENTSOF(target_table))
return target_table[handle];
return NULL;
}
int manager_handle_action(
Manager *m,
InhibitWhat inhibit_key,
@@ -51,21 +69,11 @@ int manager_handle_action(
[HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...",
};
static const char * const target_table[_HANDLE_ACTION_MAX] = {
[HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
[HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
[HANDLE_HALT] = SPECIAL_HALT_TARGET,
[HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
[HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
[HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
[HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
[HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
InhibitWhat inhibit_operation;
Inhibitor *offending = NULL;
bool supported;
const char *target;
int r;
assert(m);
@@ -97,7 +105,6 @@ int manager_handle_action(
/* Locking is handled differently from the rest. */
if (handle == HANDLE_LOCK) {
if (!is_edge)
return 0;
@@ -129,6 +136,8 @@ int manager_handle_action(
return -EALREADY;
}
assert_se(target = manager_target_for_action(handle));
inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,
HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
@@ -160,7 +169,7 @@ int manager_handle_action(
log_info("%s", message_table[handle]);
r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error);
r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
if (r < 0) {
log_error("Failed to execute operation: %s", bus_error_message(&error, r));
return r;
@@ -179,7 +188,7 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
[HANDLE_HIBERNATE] = "hibernate",
[HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
[HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
[HANDLE_LOCK] = "lock"
[HANDLE_LOCK] = "lock",
};
DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
+1
View File
@@ -48,4 +48,5 @@ int manager_handle_action(
const char* handle_action_to_string(HandleAction h) _const_;
HandleAction handle_action_from_string(const char *s) _pure_;
const char* manager_target_for_action(HandleAction handle);
int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+32
View File
@@ -29,6 +29,7 @@
#include "audit-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-unit-util.h"
#include "bus-util.h"
#include "dirent-util.h"
#include "efivars.h"
@@ -1705,6 +1706,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
InhibitWhat w,
sd_bus_error *error) {
_cleanup_free_ char *load_state = NULL;
bool delayed;
int r;
@@ -1714,6 +1716,15 @@ int bus_manager_shutdown_or_sleep_now_or_later(
assert(w <= _INHIBIT_WHAT_MAX);
assert(!m->action_job);
r = unit_load_state(m->bus, unit_name, &load_state);
if (r < 0)
return r;
if (!streq(load_state, "loaded")) {
log_notice("Unit %s is %s, refusing operation.", unit_name, load_state);
return -EACCES;
}
/* Tell everybody to prepare for shutdown/sleep */
(void) send_prepare_for(m, w, true);
@@ -2235,6 +2246,7 @@ static int method_can_shutdown_or_sleep(
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
HandleAction handle;
bool multiple_sessions, challenge, blocked;
const char *result = NULL;
uid_t uid;
@@ -2271,6 +2283,25 @@ static int method_can_shutdown_or_sleep(
multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
handle = handle_action_from_string(sleep_verb);
if (handle >= 0) {
const char *target;
target = manager_target_for_action(handle);
if (target) {
_cleanup_free_ char *load_state = NULL;
r = unit_load_state(m->bus, target, &load_state);
if (r < 0)
return r;
if (!streq(load_state, "loaded")) {
result = "no";
goto finish;
}
}
}
if (multiple_sessions) {
r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
@@ -2313,6 +2344,7 @@ static int method_can_shutdown_or_sleep(
result = "no";
}
finish:
return sd_bus_reply_method_return(message, "s", result);
}