From c8c8ee85af35f1e9593a7397cfa289a126dfcb8c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 10 Apr 2018 13:15:00 +0200 Subject: [PATCH] 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 --- src/login/logind-action.c | 37 +++++++++++++++++++++++-------------- src/login/logind-action.h | 1 + src/login/logind-dbus.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/login/logind-action.c b/src/login/logind-action.c index da38a2c1457..1e40282004b 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -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); diff --git a/src/login/logind-action.h b/src/login/logind-action.h index 9f5dee684cf..d483e434787 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -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); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 94c8f1f1420..acf05601a21 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -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); } -- 2.39.2