diff --git a/man/homectl.xml b/man/homectl.xml index 568f077c05..610c69ccc1 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -1184,6 +1184,19 @@ + + adopt PATH [PATH…] + + Adopts one or more existing home directories on the local system. Takes one or more paths to + *.home LUKS home directories or *.homedir/ standalone home + directories or subvolumes previously created by systemd-homed and makes them + available locally for login. The referenced files are not moved. This is an alternative for moving + such home directories into /home/ (where they would be picked up + automatically). + + + + remove USER diff --git a/man/org.freedesktop.home1.xml b/man/org.freedesktop.home1.xml index e0e9eef982..1d73e78d39 100644 --- a/man/org.freedesktop.home1.xml +++ b/man/org.freedesktop.home1.xml @@ -70,6 +70,8 @@ node /org/freedesktop/home1 { @org.freedesktop.systemd1.Privileged("true") DeactivateHome(in s user_name); RegisterHome(in s user_record); + AdoptHome(in s image_path, + in t flags); UnregisterHome(in s user_name); CreateHome(in s user_record); CreateHomeEx(in s user_record, @@ -160,6 +162,8 @@ node /org/freedesktop/home1 { + + @@ -274,6 +278,12 @@ node /org/freedesktop/home1 { is useful to register home directories locally that are not located where systemd-homed.service would find them automatically. + AdoptHome() also registers a new home directory locally. It takes a path to + a home directory itself, and will register it locally. This only works for *.home + and *.homedir/ home directories. This operation is done automatically for all such + home areas showing up in /home/, but may be requested explicitly with this call for + directories elsewhere. The flags must be set to zero, currently. + UnregisterHome() unregisters an existing home directory. It takes a user name as argument and undoes what RegisterHome() does. It does not attempt to remove the home directory itself, it just unregisters it with the local system. Note that if the home @@ -633,9 +643,9 @@ node /org/freedesktop/home1/home { The Manager Object ActivateHomeIfReferenced(), RefHomeUnrestricted(), CreateHomeEx(), and UpdateHomeEx() were added in version 256. - ListSigningKeys(), GetSigningKey(), - AddSigningKey(), and RemoveSigningKey() were added in version - 258. + AdoptHome(), ListSigningKeys(), + GetSigningKey(), AddSigningKey(), and + RemoveSigningKey() were added in version 258. Home Objects diff --git a/shell-completion/bash/homectl b/shell-completion/bash/homectl index 6219f25594..3bd29fc808 100644 --- a/shell-completion/bash/homectl +++ b/shell-completion/bash/homectl @@ -173,7 +173,7 @@ _homectl() { fi local -A VERBS=( - [STANDALONE]='list lock-all' + [STANDALONE]='list lock-all adopt' [CREATE]='create' [NAMES]='activate deactivate inspect authenticate remove lock unlock' [NAME]='update passwd' diff --git a/src/home/homectl.c b/src/home/homectl.c index 22570819a4..123fcd9998 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -1564,6 +1564,38 @@ static int create_home(int argc, char *argv[], void *userdata) { return create_home_common(/* input= */ NULL, /* show_enforce_password_policy_hint= */ true); } +static int verb_adopt_home(int argc, char *argv[], void *userdata) { + int r, ret = 0; + + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + r = acquire_bus(&bus); + if (r < 0) + return r; + + (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); + + STRV_FOREACH(i, strv_skip(argv, 1)) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + r = bus_message_new_method_call(bus, &m, bus_mgr, "AdoptHome"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "st", *i, UINT64_C(0)); + if (r < 0) + return bus_log_create_error(r); + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL); + if (r < 0) { + log_error_errno(r, "Failed to adopt home: %s", bus_error_message(&error, r)); + if (ret == 0) + ret = r; + } + } + + return ret; +} + static int remove_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; @@ -2808,6 +2840,7 @@ static int help(int argc, char *argv[], void *userdata) { " inspect USER… Inspect a home area\n" " authenticate USER… Authenticate a home area\n" " create USER Create a home area\n" + " adopt PATH… Add an existing home area on this system\n" " remove USER… Remove a home area\n" " update USER Update a home area\n" " passwd USER Change password of a home area\n" @@ -5242,6 +5275,7 @@ static int run(int argc, char *argv[]) { { "inspect", VERB_ANY, VERB_ANY, 0, inspect_home }, { "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home }, { "create", VERB_ANY, 2, 0, create_home }, + { "adopt", VERB_ANY, VERB_ANY, 0, verb_adopt_home }, { "remove", 2, VERB_ANY, 0, remove_home }, { "update", VERB_ANY, 2, 0, update_home }, { "passwd", VERB_ANY, 2, 0, passwd_home }, diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c index 5966758a56..a3fe2b6a69 100644 --- a/src/home/homed-manager-bus.c +++ b/src/home/homed-manager-bus.c @@ -519,6 +519,47 @@ static int method_register_home( return sd_bus_reply_method_return(message, NULL); } +static int method_adopt_home( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = ASSERT_PTR(userdata); + int r; + + assert(message); + + const char *image_path = NULL; + uint64_t flags = 0; + r = sd_bus_message_read(message, "st", &image_path, &flags); + if (r < 0) + return r; + + if (!path_is_absolute(image_path) || !path_is_safe(image_path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path is not absolute or not valid: %s", image_path); + if (flags != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags field must be zero."); + + r = bus_verify_polkit_async( + message, + "org.freedesktop.home1.create-home", + /* details= */ NULL, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = manager_adopt_home(m, image_path); + if (r == -EMEDIUMTYPE) + return sd_bus_error_setf(error, BUS_ERROR_UNRECOGNIZED_HOME_FORMAT, "Unrecognized format of home directory: %s", image_path); + if (r < 0) + 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); } @@ -1091,6 +1132,11 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_NO_RESULT, method_register_home, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("AdoptHome", + SD_BUS_ARGS("s", image_path, "t", flags), + SD_BUS_NO_RESULT, + method_adopt_home, + SD_BUS_VTABLE_UNPRIVILEGED), /* Remove the JSON record from homed, but don't remove actual $HOME */ SD_BUS_METHOD_WITH_ARGS("UnregisterHome", diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index a4512b8d26..9198368cfd 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -177,7 +177,7 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event else if (FLAGS_SET(event->mask, IN_MOVED_TO)) log_debug("%s has been moved in, having a look.", j); - (void) manager_assess_image(m, -1, get_home_root(), event->name); + (void) manager_assess_image(m, /* dir_fd= */ -EBADF, get_home_root(), event->name); (void) bus_manager_emit_auto_login_changed(m); } @@ -841,6 +841,10 @@ static int manager_assess_image( assert(dir_path); assert(dentry_name); + /* Maybe registers the specified .home or .homedir as a home we manage. Returns: + * + * -EMEDIUMTYPE: Not a dir with .homedir suffix or a file with .home suffix */ + luks_suffix = endswith(dentry_name, ".home"); if (luks_suffix) directory_suffix = NULL; @@ -849,7 +853,7 @@ static int manager_assess_image( /* Early filter out: by name */ if (!luks_suffix && !directory_suffix) - return 0; + return -EMEDIUMTYPE; path = path_join(dir_path, dentry_name); if (!path) @@ -868,7 +872,7 @@ static int manager_assess_image( _cleanup_free_ char *n = NULL, *user_name = NULL, *realm = NULL; if (!luks_suffix) - return 0; + return -EMEDIUMTYPE; n = strndup(dentry_name, luks_suffix - dentry_name); if (!n) @@ -876,7 +880,7 @@ static int manager_assess_image( r = split_user_name_realm(n, &user_name, &realm); if (r == -EINVAL) /* Not the right format: ignore */ - return 0; + return -EMEDIUMTYPE; if (r < 0) return log_error_errno(r, "Failed to split image name into user name/realm: %m"); @@ -889,7 +893,7 @@ static int manager_assess_image( UserStorage storage; if (!directory_suffix) - return 0; + return -EMEDIUMTYPE; n = strndup(dentry_name, directory_suffix - dentry_name); if (!n) @@ -897,7 +901,7 @@ static int manager_assess_image( r = split_user_name_realm(n, &user_name, &realm); if (r == -EINVAL) /* Not the right format: ignore */ - return 0; + return -EMEDIUMTYPE; if (r < 0) return log_error_errno(r, "Failed to split image name into user name/realm: %m"); @@ -939,7 +943,26 @@ static int manager_assess_image( return manager_add_home_by_image(m, user_name, realm, path, NULL, storage, st.st_uid); } - return 0; + return -EMEDIUMTYPE; +} + +int manager_adopt_home(Manager *m, const char *path) { + int r; + + assert(m); + assert(path); + + _cleanup_free_ char *fn = NULL; + r = path_extract_filename(path, &fn); + if (r < 0) + return r; + + _cleanup_free_ char *dir = NULL; + r = path_extract_directory(path, &dir); + if (r < 0) + return r; + + return manager_assess_image(m, /* dir_fd= */ -EBADF, dir, fn); } int manager_enumerate_images(Manager *m) { diff --git a/src/home/homed-manager.h b/src/home/homed-manager.h index 8f2c3d2fd7..b780094d0d 100644 --- a/src/home/homed-manager.h +++ b/src/home/homed-manager.h @@ -87,6 +87,8 @@ int manager_reschedule_rebalance(Manager *m); int manager_verify_user_record(Manager *m, UserRecord *hr); +int manager_adopt_home(Manager *m, const char *path); + int manager_acquire_key_pair(Manager *m); int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus_error *error); diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index c0f5aff5ea..c66fded7f1 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -151,6 +151,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_REBALANCE_NOT_NEEDED, EALREADY), SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_REFERENCED, EBADR), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_KEY, ENOKEY), + SD_BUS_ERROR_MAP(BUS_ERROR_UNRECOGNIZED_HOME_FORMAT, EMEDIUMTYPE), SD_BUS_ERROR_MAP(BUS_ERROR_NO_UPDATE_CANDIDATE, EALREADY), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 6322d68ad9..9148f0aba9 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -157,6 +157,7 @@ #define BUS_ERROR_REBALANCE_NOT_NEEDED "org.freedesktop.home1.RebalanceNotNeeded" #define BUS_ERROR_HOME_NOT_REFERENCED "org.freedesktop.home1.HomeNotReferenced" #define BUS_ERROR_NO_SUCH_KEY "org.freedesktop.home1.NoSuchKey" +#define BUS_ERROR_UNRECOGNIZED_HOME_FORMAT "org.freedesktop.home1.UnrecognizedHomeFormat" #define BUS_ERROR_NO_UPDATE_CANDIDATE "org.freedesktop.sysupdate1.NoCandidate"