Systemd/src/home/homed-manager-bus.c
Lennart Poettering d1f6e01e47 homed: explicitly deactivate all home directories on shutdown
Let's explicitly deactivate all home dirs on shutdown, in order to
properly synchronizing unmounting and avoiding blocking devices.

Previously, we'd rely on automatic deactivation when home directories
become unused. However, that scheme is asynchronous, and ongoing
deactviations might conflicts with attempts to unmount /home. Let's fix
that by providing an explicit service systemd-homed-activate.service
whose only job is to have a ExecStop= line that explicitly deactivates
all home directories on shutdown. This service can the be ordered after
home.mount and similar, ensuring that we'll first deactivate all homes
before deactivating /home itself during shutdown.

This is kept separate from systemd-homed.service so that it is possible
to restart systemd-homed.service without deactivating all home
directories.

Fixes: #16842
2020-09-30 14:37:52 +02:00

900 lines
34 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
#include <linux/capability.h>
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-polkit.h"
#include "format-util.h"
#include "homed-bus.h"
#include "homed-home-bus.h"
#include "homed-manager-bus.h"
#include "homed-manager.h"
#include "strv.h"
#include "user-record-sign.h"
#include "user-record-util.h"
#include "user-util.h"
static int property_get_auto_login(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = userdata;
Home *h;
int r;
assert(bus);
assert(reply);
assert(m);
r = sd_bus_message_open_container(reply, 'a', "(sso)");
if (r < 0)
return r;
HASHMAP_FOREACH(h, m->homes_by_name) {
_cleanup_(strv_freep) char **seats = NULL;
_cleanup_free_ char *home_path = NULL;
char **s;
r = home_auto_login(h, &seats);
if (r < 0) {
log_debug_errno(r, "Failed to determine whether home '%s' is candidate for auto-login, ignoring: %m", h->user_name);
continue;
}
if (!r)
continue;
r = bus_home_path(h, &home_path);
if (r < 0)
return log_error_errno(r, "Failed to generate home bus path: %m");
STRV_FOREACH(s, seats) {
r = sd_bus_message_append(reply, "(sso)", h->user_name, *s, home_path);
if (r < 0)
return r;
}
}
return sd_bus_message_close_container(reply);
}
static int method_get_home_by_name(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
const char *user_name;
Manager *m = userdata;
Home *h;
int r;
assert(message);
assert(m);
r = sd_bus_message_read(message, "s", &user_name);
if (r < 0)
return r;
if (!valid_user_group_name(user_name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
h = hashmap_get(m->homes_by_name, user_name);
if (!h)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
r = bus_home_path(h, &path);
if (r < 0)
return r;
return sd_bus_reply_method_return(
message, "usussso",
(uint32_t) h->uid,
home_state_to_string(home_get_state(h)),
h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
h->record ? user_record_real_name(h->record) : NULL,
h->record ? user_record_home_directory(h->record) : NULL,
h->record ? user_record_shell(h->record) : NULL,
path);
}
static int method_get_home_by_uid(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
uint32_t uid;
int r;
Home *h;
assert(message);
assert(m);
r = sd_bus_message_read(message, "u", &uid);
if (r < 0)
return r;
if (!uid_is_valid(uid))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
if (!h)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
/* Note that we don't use bus_home_path() here, but build the path manually, since if we are queried
* for a UID we should also generate the bus path with a UID, and bus_home_path() uses our more
* typical bus path by name. */
if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
return -ENOMEM;
return sd_bus_reply_method_return(
message, "ssussso",
h->user_name,
home_state_to_string(home_get_state(h)),
h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
h->record ? user_record_real_name(h->record) : NULL,
h->record ? user_record_home_directory(h->record) : NULL,
h->record ? user_record_shell(h->record) : NULL,
path);
}
static int method_list_homes(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Home *h;
int r;
assert(message);
assert(m);
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(susussso)");
if (r < 0)
return r;
HASHMAP_FOREACH(h, m->homes_by_uid) {
_cleanup_free_ char *path = NULL;
r = bus_home_path(h, &path);
if (r < 0)
return r;
r = sd_bus_message_append(
reply, "(susussso)",
h->user_name,
(uint32_t) h->uid,
home_state_to_string(home_get_state(h)),
h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
h->record ? user_record_real_name(h->record) : NULL,
h->record ? user_record_home_directory(h->record) : NULL,
h->record ? user_record_shell(h->record) : NULL,
path);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
static int method_get_user_record_by_name(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_free_ char *json = NULL, *path = NULL;
Manager *m = userdata;
const char *user_name;
bool incomplete;
Home *h;
int r;
assert(message);
assert(m);
r = sd_bus_message_read(message, "s", &user_name);
if (r < 0)
return r;
if (!valid_user_group_name(user_name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
h = hashmap_get(m->homes_by_name, user_name);
if (!h)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
r = bus_home_get_record_json(h, message, &json, &incomplete);
if (r < 0)
return r;
r = bus_home_path(h, &path);
if (r < 0)
return r;
return sd_bus_reply_method_return(
message, "sbo",
json,
incomplete,
path);
}
static int method_get_user_record_by_uid(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_free_ char *json = NULL, *path = NULL;
Manager *m = userdata;
bool incomplete;
uint32_t uid;
Home *h;
int r;
assert(message);
assert(m);
r = sd_bus_message_read(message, "u", &uid);
if (r < 0)
return r;
if (!uid_is_valid(uid))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
if (!h)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
r = bus_home_get_record_json(h, message, &json, &incomplete);
if (r < 0)
return r;
if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
return -ENOMEM;
return sd_bus_reply_method_return(
message, "sbo",
json,
incomplete,
path);
}
static int generic_home_method(
Manager *m,
sd_bus_message *message,
sd_bus_message_handler_t handler,
sd_bus_error *error) {
const char *user_name;
Home *h;
int r;
r = sd_bus_message_read(message, "s", &user_name);
if (r < 0)
return r;
if (!valid_user_group_name(user_name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
h = hashmap_get(m->homes_by_name, user_name);
if (!h)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
return handler(message, h, error);
}
static int method_activate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_activate, error);
}
static int method_deactivate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_deactivate, error);
}
static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd_bus_error *error) {
_cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
struct passwd *pw;
struct group *gr;
bool signed_locally;
Home *other;
int r;
assert(m);
assert(hr);
assert(ret);
r = user_record_is_supported(hr, error);
if (r < 0)
return r;
other = hashmap_get(m->homes_by_name, hr->user_name);
if (other)
return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists already, refusing.", hr->user_name);
pw = getpwnam(hr->user_name);
if (pw)
return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists in the NSS user database, refusing.", hr->user_name);
gr = getgrnam(hr->user_name);
if (gr)
return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s conflicts with an NSS group by the same name, refusing.", hr->user_name);
r = manager_verify_user_record(m, hr);
switch (r) {
case USER_RECORD_UNSIGNED:
/* If the record is unsigned, then let's sign it with our own key */
r = manager_sign_user_record(m, hr, &signed_hr, error);
if (r < 0)
return r;
hr = signed_hr;
_fallthrough_;
case USER_RECORD_SIGNED_EXCLUSIVE:
signed_locally = true;
break;
case USER_RECORD_SIGNED:
case USER_RECORD_FOREIGN:
signed_locally = false;
break;
case -ENOKEY:
return sd_bus_error_setf(error, BUS_ERROR_BAD_SIGNATURE, "Specified user record for %s is signed by a key we don't recognize, refusing.", hr->user_name);
default:
return sd_bus_error_set_errnof(error, r, "Failed to validate signature for '%s': %m", hr->user_name);
}
if (uid_is_valid(hr->uid)) {
other = hashmap_get(m->homes_by_uid, UID_TO_PTR(hr->uid));
if (other)
return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by home %s, refusing.", hr->uid, other->user_name);
pw = getpwuid(hr->uid);
if (pw)
return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by NSS user %s, refusing.", hr->uid, pw->pw_name);
gr = getgrgid(hr->uid);
if (gr)
return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use as GID by NSS group %s, refusing.", hr->uid, gr->gr_name);
} else {
r = manager_augment_record_with_uid(m, hr);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to acquire UID for '%s': %m", hr->user_name);
}
r = home_new(m, hr, NULL, ret);
if (r < 0)
return r;
(*ret)->signed_locally = signed_locally;
return r;
}
static int method_register_home(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
Manager *m = userdata;
Home *h;
int r;
assert(message);
assert(m);
r = bus_message_read_home_record(message, USER_RECORD_LOAD_EMBEDDED, &hr, error);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
NULL,
true,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = validate_and_allocate_home(m, hr, &h, error);
if (r < 0)
return r;
r = home_save_record(h);
if (r < 0) {
home_free(h);
return r;
}
return sd_bus_reply_method_return(message, NULL);
}
static int method_unregister_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_unregister, error);
}
static int method_create_home(
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
Manager *m = userdata;
Home *h;
int r;
assert(message);
assert(m);
r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
NULL,
true,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
r = validate_and_allocate_home(m, hr, &h, error);
if (r < 0)
return r;
r = home_create(h, hr, error);
if (r < 0)
goto fail;
assert(r == 0);
h->unregister_on_failure = true;
assert(!h->current_operation);
r = home_set_current_message(h, message);
if (r < 0)
return r;
return 1;
fail:
(void) home_unlink_record(h);
h = home_free(h);
return r;
}
static int method_realize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_realize, error);
}
static int method_remove_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_remove, error);
}
static int method_fixate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_fixate, error);
}
static int method_authenticate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_authenticate, error);
}
static int method_update_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
Manager *m = userdata;
Home *h;
int r;
assert(message);
assert(m);
r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
if (r < 0)
return r;
assert(hr->user_name);
h = hashmap_get(m->homes_by_name, hr->user_name);
if (!h)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", hr->user_name);
return bus_home_method_update_record(h, message, hr, error);
}
static int method_resize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_resize, error);
}
static int method_change_password_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_change_password, error);
}
static int method_lock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_lock, error);
}
static int method_unlock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_unlock, error);
}
static int method_acquire_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_acquire, error);
}
static int method_ref_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_ref, error);
}
static int method_release_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return generic_home_method(userdata, message, bus_home_method_release, error);
}
static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(operation_unrefp) Operation *o = NULL;
bool waiting = false;
Manager *m = userdata;
Home *h;
int r;
assert(m);
/* This is called from logind when we are preparing for system suspend. We enqueue a lock operation
* for every suitable home we have and only when all of them completed we send a reply indicating
* completion. */
HASHMAP_FOREACH(h, m->homes_by_name) {
/* Automatically suspend all homes that have at least one client referencing it that asked
* for "please suspend", and no client that asked for "please do not suspend". */
if (h->ref_event_source_dont_suspend ||
!h->ref_event_source_please_suspend)
continue;
if (!o) {
o = operation_new(OPERATION_LOCK_ALL, message);
if (!o)
return -ENOMEM;
}
log_info("Automatically locking home of user %s.", h->user_name);
r = home_schedule_operation(h, o, error);
if (r < 0)
return r;
waiting = true;
}
if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will
* be sent as soon as the last of the lock operations completed. */
return 1;
return sd_bus_reply_method_return(message, NULL);
}
static int method_deactivate_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(operation_unrefp) Operation *o = NULL;
bool waiting = false;
Manager *m = userdata;
Home *h;
int r;
assert(m);
/* This is called from systemd-homed-activate.service's ExecStop= command to ensure that all home
* directories are shutdown before the system goes down. Note that we don't do this from
* systemd-homed.service itself since we want to allow restarting of it without tearing down all home
* directories. */
HASHMAP_FOREACH(h, m->homes_by_name) {
if (!o) {
o = operation_new(OPERATION_DEACTIVATE_ALL, message);
if (!o)
return -ENOMEM;
}
log_info("Automatically deactivating home of user %s.", h->user_name);
r = home_schedule_operation(h, o, error);
if (r < 0)
return r;
waiting = true;
}
if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will be
* sent as soon as the last of the deactivation operations completed. */
return 1;
return sd_bus_reply_method_return(message, NULL);
}
static const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD_WITH_NAMES("GetHomeByName",
"s",
SD_BUS_PARAM(user_name),
"usussso",
SD_BUS_PARAM(uid)
SD_BUS_PARAM(home_state)
SD_BUS_PARAM(gid)
SD_BUS_PARAM(real_name)
SD_BUS_PARAM(home_directory)
SD_BUS_PARAM(shell)
SD_BUS_PARAM(bus_path),
method_get_home_by_name,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("GetHomeByUID",
"u",
SD_BUS_PARAM(uid),
"ssussso",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(home_state)
SD_BUS_PARAM(gid)
SD_BUS_PARAM(real_name)
SD_BUS_PARAM(home_directory)
SD_BUS_PARAM(shell)
SD_BUS_PARAM(bus_path),
method_get_home_by_uid,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("GetUserRecordByName",
"s",
SD_BUS_PARAM(user_name),
"sbo",
SD_BUS_PARAM(user_record)
SD_BUS_PARAM(incomplete)
SD_BUS_PARAM(bus_path),
method_get_user_record_by_name,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
SD_BUS_METHOD_WITH_NAMES("GetUserRecordByUID",
"u",
SD_BUS_PARAM(uid),
"sbo",
SD_BUS_PARAM(user_record)
SD_BUS_PARAM(incomplete)
SD_BUS_PARAM(bus_path),
method_get_user_record_by_uid,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
SD_BUS_METHOD_WITH_NAMES("ListHomes",
NULL,,
"a(susussso)",
SD_BUS_PARAM(home_areas),
method_list_homes,
SD_BUS_VTABLE_UNPRIVILEGED),
/* The following methods directly execute an operation on a home area, without ref-counting, queueing
* or anything, and are accessible through homectl. */
SD_BUS_METHOD_WITH_NAMES("ActivateHome",
"ss",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(secret),
NULL,,
method_activate_home,
SD_BUS_VTABLE_SENSITIVE),
SD_BUS_METHOD_WITH_NAMES("DeactivateHome",
"s",
SD_BUS_PARAM(user_name),
NULL,,
method_deactivate_home,
0),
/* Add the JSON record to homed, but don't create actual $HOME */
SD_BUS_METHOD_WITH_NAMES("RegisterHome",
"s",
SD_BUS_PARAM(user_record),
NULL,,
method_register_home,
SD_BUS_VTABLE_UNPRIVILEGED),
/* Remove the JSON record from homed, but don't remove actual $HOME */
SD_BUS_METHOD_WITH_NAMES("UnregisterHome",
"s",
SD_BUS_PARAM(user_name),
NULL,,
method_unregister_home,
SD_BUS_VTABLE_UNPRIVILEGED),
/* Add JSON record, and create $HOME for it */
SD_BUS_METHOD_WITH_NAMES("CreateHome",
"s",
SD_BUS_PARAM(user_record),
NULL,,
method_create_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
/* Create $HOME for already registered JSON entry */
SD_BUS_METHOD_WITH_NAMES("RealizeHome",
"ss",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(secret),
NULL,,
method_realize_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
/* Remove the JSON record and remove $HOME */
SD_BUS_METHOD_WITH_NAMES("RemoveHome",
"s",
SD_BUS_PARAM(user_name),
NULL,,
method_remove_home,
SD_BUS_VTABLE_UNPRIVILEGED),
/* Investigate $HOME and propagate contained JSON record into our database */
SD_BUS_METHOD_WITH_NAMES("FixateHome",
"ss",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(secret),
NULL,,
method_fixate_home,
SD_BUS_VTABLE_SENSITIVE),
/* Just check credentials */
SD_BUS_METHOD_WITH_NAMES("AuthenticateHome",
"ss",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(secret),
NULL,,
method_authenticate_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
/* Update the JSON record of existing user */
SD_BUS_METHOD_WITH_NAMES("UpdateHome",
"s",
SD_BUS_PARAM(user_record),
NULL,,
method_update_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
SD_BUS_METHOD_WITH_NAMES("ResizeHome",
"sts",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(size)
SD_BUS_PARAM(secret),
NULL,,
method_resize_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
SD_BUS_METHOD_WITH_NAMES("ChangePasswordHome",
"sss",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(new_secret)
SD_BUS_PARAM(old_secret),
NULL,,
method_change_password_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
/* Prepare active home for system suspend: flush out passwords, suspend access */
SD_BUS_METHOD_WITH_NAMES("LockHome",
"s",
SD_BUS_PARAM(user_name),
NULL,,
method_lock_home,
0),
/* Make $HOME usable after system resume again */
SD_BUS_METHOD_WITH_NAMES("UnlockHome",
"ss",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(secret),
NULL,,
method_unlock_home,
SD_BUS_VTABLE_SENSITIVE),
/* The following methods implement ref-counted activation, and are what the PAM module and "homectl
* with" use. In contrast to the methods above which fail if an operation is already being executed
* on a home directory, these ones will queue the request, and are thus more reliable. Moreover,
* they are a bit smarter: AcquireHome() will fixate, activate, unlock, or authenticate depending on
* the state of the home area, so that the end result is always the same (i.e. the home directory is
* accessible), and we always validate the specified passwords. RefHome() will not authenticate, and
* thus only works if the home area is already active. */
SD_BUS_METHOD_WITH_NAMES("AcquireHome",
"ssb",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(secret)
SD_BUS_PARAM(please_suspend),
"h",
SD_BUS_PARAM(send_fd),
method_acquire_home,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
SD_BUS_METHOD_WITH_NAMES("RefHome",
"sb",
SD_BUS_PARAM(user_name)
SD_BUS_PARAM(please_suspend),
"h",
SD_BUS_PARAM(send_fd),
method_ref_home,
0),
SD_BUS_METHOD_WITH_NAMES("ReleaseHome",
"s",
SD_BUS_PARAM(user_name),
NULL,,
method_release_home,
0),
/* An operation that acts on all homes that allow it */
SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
SD_BUS_METHOD("DeactivateAllHomes", NULL, NULL, method_deactivate_all_homes, 0),
SD_BUS_VTABLE_END
};
const BusObjectImplementation manager_object = {
"/org/freedesktop/home1",
"org.freedesktop.home1.Manager",
.vtables = BUS_VTABLES(manager_vtable),
.children = BUS_IMPLEMENTATIONS(&home_object),
};
static int on_deferred_auto_login(sd_event_source *s, void *userdata) {
Manager *m = userdata;
int r;
assert(m);
m->deferred_auto_login_event_source = sd_event_source_unref(m->deferred_auto_login_event_source);
r = sd_bus_emit_properties_changed(
m->bus,
"/org/freedesktop/home1",
"org.freedesktop.home1.Manager",
"AutoLogin", NULL);
if (r < 0)
log_warning_errno(r, "Failed to send AutoLogin property change event, ignoring: %m");
return 0;
}
int bus_manager_emit_auto_login_changed(Manager *m) {
int r;
assert(m);
if (m->deferred_auto_login_event_source)
return 0;
if (!m->event)
return 0;
if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
return 0;
r = sd_event_add_defer(m->event, &m->deferred_auto_login_event_source, on_deferred_auto_login, m);
if (r < 0)
return log_error_errno(r, "Failed to allocate auto login event source: %m");
r = sd_event_source_set_priority(m->deferred_auto_login_event_source, SD_EVENT_PRIORITY_IDLE+10);
if (r < 0)
log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
(void) sd_event_source_set_description(m->deferred_auto_login_event_source, "deferred-auto-login");
return 1;
}