logind: move verify_shutdown_creds() to logind-shutdown.c
Move verify_shutdown_creds() and its helper have_multiple_sessions() from logind-dbus.c to logind-shutdown.c so that they can be reused by the varlink transport. No functional changes. Also prefix both with `manager_` now that they are public.
This commit is contained in:
+3
-136
@@ -1863,24 +1863,6 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int have_multiple_sessions(
|
||||
Manager *m,
|
||||
uid_t uid) {
|
||||
|
||||
Session *session;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Check for other users' sessions. Greeter sessions do not
|
||||
* count, and non-login sessions do not count either. */
|
||||
HASHMAP_FOREACH(session, m->sessions)
|
||||
if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) &&
|
||||
session->user->user_record->uid != uid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int bus_manager_log_shutdown(
|
||||
Manager *m,
|
||||
const HandleActionData *a) {
|
||||
@@ -2186,121 +2168,6 @@ int bus_manager_shutdown_or_sleep_now_or_later(
|
||||
return r;
|
||||
}
|
||||
|
||||
static int verify_shutdown_creds(
|
||||
Manager *m,
|
||||
sd_bus_message *message,
|
||||
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;
|
||||
uid_t uid;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(a);
|
||||
assert(message);
|
||||
|
||||
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;
|
||||
|
||||
r = have_multiple_sessions(m, uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
multiple_sessions = r > 0;
|
||||
blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending);
|
||||
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 (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,
|
||||
* ignore that error and allow the decision to be revealed below. */
|
||||
if (blocked && r == -EBUSY)
|
||||
error_or_denial = true;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
}
|
||||
|
||||
if (blocked) {
|
||||
PolkitFlags polkit_flags = 0;
|
||||
|
||||
/* With a strong inhibitor, if the skip flag is not set, reject outright.
|
||||
* With a weak inhibitor, if root is asking and the root flag is set, reject outright.
|
||||
* All else, check polkit first. */
|
||||
if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) &&
|
||||
(offending->mode != INHIBIT_BLOCK_WEAK ||
|
||||
(uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS))))
|
||||
return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK,
|
||||
"Operation denied due to active block inhibitor");
|
||||
|
||||
/* We want to always ask here, even for root, to only allow bypassing if explicitly allowed
|
||||
* by polkit, unless a weak blocker is used, in which case it will be authorized. */
|
||||
if (offending->mode != INHIBIT_BLOCK_WEAK)
|
||||
polkit_flags |= POLKIT_ALWAYS_QUERY;
|
||||
|
||||
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 (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
}
|
||||
|
||||
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 (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
}
|
||||
|
||||
/* If error_or_denial was set above, it means that a polkit denial or
|
||||
* error was deferred for a future call to bus_verify_polkit_async_full()
|
||||
* to catch. In any case, it also means that the payload guarded by
|
||||
* these polkit calls should never be executed, and hence we should
|
||||
* never reach this point. */
|
||||
assert(!error_or_denial);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
|
||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||
int r;
|
||||
@@ -2439,7 +2306,7 @@ static int method_do_shutdown_or_sleep(
|
||||
} else if (!a)
|
||||
assert_se(a = handle_action_lookup(action));
|
||||
|
||||
r = verify_shutdown_creds(m, message, a, flags, error);
|
||||
r = manager_verify_shutdown_creds(m, message, a, flags, error);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
@@ -2793,7 +2660,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
||||
assert_se(a = handle_action_lookup(handle));
|
||||
assert(a->polkit_action);
|
||||
|
||||
r = verify_shutdown_creds(m, message, a, 0, error);
|
||||
r = manager_verify_shutdown_creds(m, message, a, 0, error);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
@@ -2943,7 +2810,7 @@ static int method_can_shutdown_or_sleep(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = have_multiple_sessions(m, uid);
|
||||
r = manager_have_multiple_sessions(m, uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -1,12 +1,160 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-polkit.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "login-util.h"
|
||||
#include "logind.h"
|
||||
#include "logind-dbus.h"
|
||||
#include "logind-inhibit.h"
|
||||
#include "logind-session.h"
|
||||
#include "logind-shutdown.h"
|
||||
#include "logind-user.h"
|
||||
#include "user-record.h"
|
||||
|
||||
int manager_have_multiple_sessions(
|
||||
Manager *m,
|
||||
uid_t uid) {
|
||||
|
||||
Session *session;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Check for other users' sessions. Greeter sessions do not
|
||||
* count, and non-login sessions do not count either. */
|
||||
HASHMAP_FOREACH(session, m->sessions)
|
||||
if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) &&
|
||||
session->user->user_record->uid != uid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int manager_verify_shutdown_creds(
|
||||
Manager *m,
|
||||
sd_bus_message *message,
|
||||
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;
|
||||
uid_t uid;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(a);
|
||||
assert(message);
|
||||
|
||||
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;
|
||||
|
||||
r = manager_have_multiple_sessions(m, uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
multiple_sessions = r > 0;
|
||||
blocked = manager_is_inhibited(m, a->inhibit_what, NULL, /* flags= */ 0, uid, &offending);
|
||||
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 (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,
|
||||
* ignore that error and allow the decision to be revealed below. */
|
||||
if (blocked && r == -EBUSY)
|
||||
error_or_denial = true;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
}
|
||||
|
||||
if (blocked) {
|
||||
PolkitFlags polkit_flags = 0;
|
||||
|
||||
/* With a strong inhibitor, if the skip flag is not set, reject outright.
|
||||
* With a weak inhibitor, if root is asking and the root flag is set, reject outright.
|
||||
* All else, check polkit first. */
|
||||
if (!FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) &&
|
||||
(offending->mode != INHIBIT_BLOCK_WEAK ||
|
||||
(uid == 0 && FLAGS_SET(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS)))) {
|
||||
if (error)
|
||||
return sd_bus_error_set(error, BUS_ERROR_BLOCKED_BY_INHIBITOR_LOCK,
|
||||
"Operation denied due to active block inhibitor");
|
||||
else
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* We want to always ask here, even for root, to only allow bypassing if explicitly allowed
|
||||
* by polkit, unless a weak blocker is used, in which case it will be authorized. */
|
||||
if (offending->mode != INHIBIT_BLOCK_WEAK)
|
||||
polkit_flags |= POLKIT_ALWAYS_QUERY;
|
||||
|
||||
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 (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
}
|
||||
|
||||
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 (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
}
|
||||
|
||||
/* If error_or_denial was set above, it means that a polkit denial or
|
||||
* error was deferred for a future call to bus_verify_polkit_async_full()
|
||||
* to catch. In any case, it also means that the payload guarded by
|
||||
* these polkit calls should never be executed, and hence we should
|
||||
* never reach this point. */
|
||||
assert(!error_or_denial);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void manager_reset_scheduled_shutdown(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
@@ -5,4 +5,6 @@
|
||||
|
||||
#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled"
|
||||
|
||||
int manager_have_multiple_sessions(Manager *m, uid_t uid);
|
||||
int manager_verify_shutdown_creds(Manager *m, sd_bus_message *message, const HandleActionData *a, uint64_t flags, sd_bus_error *error);
|
||||
void manager_reset_scheduled_shutdown(Manager *m);
|
||||
|
||||
Reference in New Issue
Block a user