#include "sd-bus.h"
#include "sd-event.h"
+#include "sd-varlink.h"
-#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-polkit.h"
#include "fs-util.h"
int manager_verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
+ sd_varlink *link,
const HandleActionData *a,
uint64_t flags,
sd_bus_error *error) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
bool multiple_sessions, blocked, interactive;
_unused_ bool error_or_denial = false;
Inhibitor *offending = NULL;
assert(m);
assert(a);
- assert(message);
+ assert(!!message != !!link); /* exactly one transport */
+ assert(!link || !error); /* varlink doesn't use sd_bus_error */
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
- if (r < 0)
- return r;
+ if (message) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- r = sd_bus_creds_get_euid(creds, &uid);
- if (r < 0)
- return r;
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_euid(creds, &uid);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_varlink_get_peer_uid(link, &uid);
+ if (r < 0)
+ return r;
+ }
r = manager_have_multiple_sessions(m, uid);
if (r < 0)
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
- r = bus_verify_polkit_async_full(
- message,
- a->polkit_action_multiple_sessions,
- /* details= */ NULL,
- /* good_user= */ UID_INVALID,
- interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
- &m->polkit_registry,
- error);
+ if (message)
+ r = bus_verify_polkit_async_full(
+ message,
+ a->polkit_action_multiple_sessions,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
+ &m->polkit_registry,
+ error);
+ else
+ r = varlink_verify_polkit_async_full(
+ link,
+ m->bus,
+ a->polkit_action_multiple_sessions,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
+ &m->polkit_registry);
+
if (r < 0) {
/* If we get -EBUSY, it means a polkit decision was made, but not for
* this action in particular. Assuming we are blocked on inhibitors,
if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) &&
(offending->mode != INHIBIT_BLOCK_WEAK ||
(uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS)))) {
+ if (link)
+ return sd_varlink_errorbo(
+ link,
+ "io.systemd.Shutdown.BlockedByInhibitor",
+ SD_JSON_BUILD_PAIR_STRING("who", offending->who),
+ SD_JSON_BUILD_PAIR_STRING("why", offending->why));
if (error)
return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK,
"Operation denied due to active block inhibitor");
- else
- return -EACCES;
+ return -EACCES;
}
/* We want to always ask here, even for root, to only allow bypassing if explicitly allowed
if (interactive)
polkit_flags |= POLKIT_ALLOW_INTERACTIVE;
- r = bus_verify_polkit_async_full(
- message,
- a->polkit_action_ignore_inhibit,
- /* details= */ NULL,
- /* good_user= */ UID_INVALID,
- polkit_flags,
- &m->polkit_registry,
- error);
+ if (message)
+ r = bus_verify_polkit_async_full(
+ message,
+ a->polkit_action_ignore_inhibit,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ polkit_flags,
+ &m->polkit_registry,
+ error);
+ else
+ r = varlink_verify_polkit_async_full(
+ link,
+ m->bus,
+ a->polkit_action_ignore_inhibit,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ polkit_flags,
+ &m->polkit_registry);
+
if (r < 0)
return r;
if (r == 0)
}
if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async_full(
- message,
- a->polkit_action,
- /* details= */ NULL,
- /* good_user= */ UID_INVALID,
- interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
- &m->polkit_registry,
- error);
+ if (message)
+ r = bus_verify_polkit_async_full(
+ message,
+ a->polkit_action,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
+ &m->polkit_registry,
+ error);
+ else
+ r = varlink_verify_polkit_async_full(
+ link,
+ m->bus,
+ a->polkit_action,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
+ &m->polkit_registry);
+
if (r < 0)
return r;
if (r == 0)
static int manager_do_shutdown_action(sd_varlink *link, sd_json_variant *parameters, HandleAction action) {
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(link));
int skip_inhibitors = -1;
- uid_t uid;
int r;
static const sd_json_dispatch_field dispatch_table[] = {
{ "skipInhibitors", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, 0, 0 },
+ VARLINK_DISPATCH_POLKIT_FIELD,
{}
};
const HandleActionData *a = handle_action_lookup(action);
assert(a);
- /* TODO: provide full polkit support (matching the D-Bus verify_shutdown_creds() with
- * multiple-sessions and inhibitor-override checks). This requires some refactor. */
- r = varlink_check_privileged_peer(link);
- if (r < 0)
- return r;
-
- r = sd_varlink_get_peer_uid(link, &uid);
- if (r < 0)
+ r = manager_verify_shutdown_creds(m, /* message= */ NULL, link, a, flags, /* error= */ NULL);
+ if (r != 0)
return r;
- /* Check for active inhibitors, mirroring the D-Bus verify_shutdown_creds() logic,
- * we need this to ensure we handle the strong INHIBIT_BLOCK
- * TODO: drop once we do the verify_shutdown_creds() */
- if (manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, /* ret_offending= */ NULL) &&
- !FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS))
- return sd_varlink_error(link, "io.systemd.Shutdown.BlockedByInhibitor", /* parameters= */ NULL);
-
if (m->delayed_action)
return sd_varlink_error(link, "io.systemd.Shutdown.AlreadyInProgress", /* parameters= */ NULL);