1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <linux/capability.h>
5 #include "alloc-util.h"
6 #include "bus-common-errors.h"
7 #include "bus-polkit.h"
8 #include "format-util.h"
10 #include "homed-home-bus.h"
11 #include "homed-manager-bus.h"
12 #include "homed-manager.h"
14 #include "user-record-sign.h"
15 #include "user-record-util.h"
16 #include "user-util.h"
18 static int property_get_auto_login(
21 const char *interface
,
23 sd_bus_message
*reply
,
25 sd_bus_error
*error
) {
27 Manager
*m
= userdata
;
36 r
= sd_bus_message_open_container(reply
, 'a', "(sso)");
40 HASHMAP_FOREACH(h
, m
->homes_by_name
, i
) {
41 _cleanup_(strv_freep
) char **seats
= NULL
;
42 _cleanup_free_
char *home_path
= NULL
;
45 r
= home_auto_login(h
, &seats
);
47 log_debug_errno(r
, "Failed to determine whether home '%s' is candidate for auto-login, ignoring: %m", h
->user_name
);
53 r
= bus_home_path(h
, &home_path
);
55 return log_error_errno(r
, "Failed to generate home bus path: %m");
57 STRV_FOREACH(s
, seats
) {
58 r
= sd_bus_message_append(reply
, "(sso)", h
->user_name
, *s
, home_path
);
64 return sd_bus_message_close_container(reply
);
67 static int method_get_home_by_name(
68 sd_bus_message
*message
,
70 sd_bus_error
*error
) {
72 _cleanup_free_
char *path
= NULL
;
73 const char *user_name
;
74 Manager
*m
= userdata
;
81 r
= sd_bus_message_read(message
, "s", &user_name
);
84 if (!valid_user_group_name(user_name
, 0))
85 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User name %s is not valid", user_name
);
87 h
= hashmap_get(m
->homes_by_name
, user_name
);
89 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_HOME
, "No home for user %s known", user_name
);
91 r
= bus_home_path(h
, &path
);
95 return sd_bus_reply_method_return(
98 home_state_to_string(home_get_state(h
)),
99 h
->record
? (uint32_t) user_record_gid(h
->record
) : GID_INVALID
,
100 h
->record
? user_record_real_name(h
->record
) : NULL
,
101 h
->record
? user_record_home_directory(h
->record
) : NULL
,
102 h
->record
? user_record_shell(h
->record
) : NULL
,
106 static int method_get_home_by_uid(
107 sd_bus_message
*message
,
109 sd_bus_error
*error
) {
111 _cleanup_free_
char *path
= NULL
;
112 Manager
*m
= userdata
;
120 r
= sd_bus_message_read(message
, "u", &uid
);
123 if (!uid_is_valid(uid
))
124 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "UID " UID_FMT
" is not valid", uid
);
126 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR(uid
));
128 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_HOME
, "No home for UID " UID_FMT
" known", uid
);
130 /* Note that we don't use bus_home_path() here, but build the path manually, since if we are queried
131 * for a UID we should also generate the bus path with a UID, and bus_home_path() uses our more
132 * typical bus path by name. */
133 if (asprintf(&path
, "/org/freedesktop/home1/home/" UID_FMT
, h
->uid
) < 0)
136 return sd_bus_reply_method_return(
139 home_state_to_string(home_get_state(h
)),
140 h
->record
? (uint32_t) user_record_gid(h
->record
) : GID_INVALID
,
141 h
->record
? user_record_real_name(h
->record
) : NULL
,
142 h
->record
? user_record_home_directory(h
->record
) : NULL
,
143 h
->record
? user_record_shell(h
->record
) : NULL
,
147 static int method_list_homes(
148 sd_bus_message
*message
,
150 sd_bus_error
*error
) {
152 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
153 Manager
*m
= userdata
;
161 r
= sd_bus_message_new_method_return(message
, &reply
);
165 r
= sd_bus_message_open_container(reply
, 'a', "(susussso)");
169 HASHMAP_FOREACH(h
, m
->homes_by_uid
, i
) {
170 _cleanup_free_
char *path
= NULL
;
172 r
= bus_home_path(h
, &path
);
176 r
= sd_bus_message_append(
180 home_state_to_string(home_get_state(h
)),
181 h
->record
? (uint32_t) user_record_gid(h
->record
) : GID_INVALID
,
182 h
->record
? user_record_real_name(h
->record
) : NULL
,
183 h
->record
? user_record_home_directory(h
->record
) : NULL
,
184 h
->record
? user_record_shell(h
->record
) : NULL
,
190 r
= sd_bus_message_close_container(reply
);
194 return sd_bus_send(NULL
, reply
, NULL
);
197 static int method_get_user_record_by_name(
198 sd_bus_message
*message
,
200 sd_bus_error
*error
) {
202 _cleanup_free_
char *json
= NULL
, *path
= NULL
;
203 Manager
*m
= userdata
;
204 const char *user_name
;
212 r
= sd_bus_message_read(message
, "s", &user_name
);
215 if (!valid_user_group_name(user_name
, 0))
216 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User name %s is not valid", user_name
);
218 h
= hashmap_get(m
->homes_by_name
, user_name
);
220 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_HOME
, "No home for user %s known", user_name
);
222 r
= bus_home_get_record_json(h
, message
, &json
, &incomplete
);
226 r
= bus_home_path(h
, &path
);
230 return sd_bus_reply_method_return(
237 static int method_get_user_record_by_uid(
238 sd_bus_message
*message
,
240 sd_bus_error
*error
) {
242 _cleanup_free_
char *json
= NULL
, *path
= NULL
;
243 Manager
*m
= userdata
;
252 r
= sd_bus_message_read(message
, "u", &uid
);
255 if (!uid_is_valid(uid
))
256 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "UID " UID_FMT
" is not valid", uid
);
258 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR(uid
));
260 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_HOME
, "No home for UID " UID_FMT
" known", uid
);
262 r
= bus_home_get_record_json(h
, message
, &json
, &incomplete
);
266 if (asprintf(&path
, "/org/freedesktop/home1/home/" UID_FMT
, h
->uid
) < 0)
269 return sd_bus_reply_method_return(
276 static int generic_home_method(
278 sd_bus_message
*message
,
279 sd_bus_message_handler_t handler
,
280 sd_bus_error
*error
) {
282 const char *user_name
;
286 r
= sd_bus_message_read(message
, "s", &user_name
);
290 if (!valid_user_group_name(user_name
, 0))
291 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User name %s is not valid", user_name
);
293 h
= hashmap_get(m
->homes_by_name
, user_name
);
295 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_HOME
, "No home for user %s known", user_name
);
297 return handler(message
, h
, error
);
300 static int method_activate_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
301 return generic_home_method(userdata
, message
, bus_home_method_activate
, error
);
304 static int method_deactivate_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
305 return generic_home_method(userdata
, message
, bus_home_method_deactivate
, error
);
308 static int validate_and_allocate_home(Manager
*m
, UserRecord
*hr
, Home
**ret
, sd_bus_error
*error
) {
309 _cleanup_(user_record_unrefp
) UserRecord
*signed_hr
= NULL
;
320 r
= user_record_is_supported(hr
, error
);
324 other
= hashmap_get(m
->homes_by_name
, hr
->user_name
);
326 return sd_bus_error_setf(error
, BUS_ERROR_USER_NAME_EXISTS
, "Specified user name %s exists already, refusing.", hr
->user_name
);
328 pw
= getpwnam(hr
->user_name
);
330 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
);
332 gr
= getgrnam(hr
->user_name
);
334 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
);
336 r
= manager_verify_user_record(m
, hr
);
339 case USER_RECORD_UNSIGNED
:
340 /* If the record is unsigned, then let's sign it with our own key */
341 r
= manager_sign_user_record(m
, hr
, &signed_hr
, error
);
348 case USER_RECORD_SIGNED_EXCLUSIVE
:
349 signed_locally
= true;
352 case USER_RECORD_SIGNED
:
353 case USER_RECORD_FOREIGN
:
354 signed_locally
= false;
358 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
);
361 return sd_bus_error_set_errnof(error
, r
, "Failed to validate signature for '%s': %m", hr
->user_name
);
364 if (uid_is_valid(hr
->uid
)) {
365 other
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR(hr
->uid
));
367 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
);
369 pw
= getpwuid(hr
->uid
);
371 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
);
373 gr
= getgrgid(hr
->uid
);
375 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
);
377 r
= manager_augment_record_with_uid(m
, hr
);
379 return sd_bus_error_set_errnof(error
, r
, "Failed to acquire UID for '%s': %m", hr
->user_name
);
382 r
= home_new(m
, hr
, NULL
, ret
);
386 (*ret
)->signed_locally
= signed_locally
;
390 static int method_register_home(
391 sd_bus_message
*message
,
393 sd_bus_error
*error
) {
395 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
396 Manager
*m
= userdata
;
403 r
= bus_message_read_home_record(message
, USER_RECORD_LOAD_EMBEDDED
, &hr
, error
);
407 r
= bus_verify_polkit_async(
410 "org.freedesktop.home1.create-home",
419 return 1; /* Will call us back */
421 r
= validate_and_allocate_home(m
, hr
, &h
, error
);
425 r
= home_save_record(h
);
431 return sd_bus_reply_method_return(message
, NULL
);
434 static int method_unregister_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
435 return generic_home_method(userdata
, message
, bus_home_method_unregister
, error
);
438 static int method_create_home(
439 sd_bus_message
*message
,
441 sd_bus_error
*error
) {
443 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
444 Manager
*m
= userdata
;
451 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
);
455 r
= bus_verify_polkit_async(
458 "org.freedesktop.home1.create-home",
467 return 1; /* Will call us back */
469 r
= validate_and_allocate_home(m
, hr
, &h
, error
);
473 r
= home_create(h
, hr
, error
);
478 h
->unregister_on_failure
= true;
479 assert(!h
->current_operation
);
481 r
= home_set_current_message(h
, message
);
488 (void) home_unlink_record(h
);
493 static int method_realize_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
494 return generic_home_method(userdata
, message
, bus_home_method_realize
, error
);
497 static int method_remove_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
498 return generic_home_method(userdata
, message
, bus_home_method_remove
, error
);
501 static int method_fixate_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
502 return generic_home_method(userdata
, message
, bus_home_method_fixate
, error
);
505 static int method_authenticate_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
506 return generic_home_method(userdata
, message
, bus_home_method_authenticate
, error
);
509 static int method_update_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
510 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
511 Manager
*m
= userdata
;
518 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
);
522 assert(hr
->user_name
);
524 h
= hashmap_get(m
->homes_by_name
, hr
->user_name
);
526 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_HOME
, "No home for user %s known", hr
->user_name
);
528 return bus_home_method_update_record(h
, message
, hr
, error
);
531 static int method_resize_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
532 return generic_home_method(userdata
, message
, bus_home_method_resize
, error
);
535 static int method_change_password_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
536 return generic_home_method(userdata
, message
, bus_home_method_change_password
, error
);
539 static int method_lock_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
540 return generic_home_method(userdata
, message
, bus_home_method_lock
, error
);
543 static int method_unlock_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
544 return generic_home_method(userdata
, message
, bus_home_method_unlock
, error
);
547 static int method_acquire_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
548 return generic_home_method(userdata
, message
, bus_home_method_acquire
, error
);
551 static int method_ref_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
552 return generic_home_method(userdata
, message
, bus_home_method_ref
, error
);
555 static int method_release_home(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
556 return generic_home_method(userdata
, message
, bus_home_method_release
, error
);
559 static int method_lock_all_homes(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
560 _cleanup_(operation_unrefp
) Operation
*o
= NULL
;
561 bool waiting
= false;
562 Manager
*m
= userdata
;
569 /* This is called from logind when we are preparing for system suspend. We enqueue a lock operation
570 * for every suitable home we have and only when all of them completed we send a reply indicating
573 HASHMAP_FOREACH(h
, m
->homes_by_name
, i
) {
575 /* Automatically suspend all homes that have at least one client referencing it that asked
576 * for "please suspend", and no client that asked for "please do not suspend". */
577 if (h
->ref_event_source_dont_suspend
||
578 !h
->ref_event_source_please_suspend
)
582 o
= operation_new(OPERATION_LOCK_ALL
, message
);
587 log_info("Automatically locking home of user %s.", h
->user_name
);
589 r
= home_schedule_operation(h
, o
, error
);
596 if (waiting
) /* At least one lock operation was enqeued, let's leave here without a reply: it will
597 * be sent as soon as the last of the lock operations completed. */
600 return sd_bus_reply_method_return(message
, NULL
);
603 const sd_bus_vtable manager_vtable
[] = {
604 SD_BUS_VTABLE_START(0),
606 SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
608 SD_BUS_METHOD("GetHomeByName", "s", "usussso", method_get_home_by_name
, SD_BUS_VTABLE_UNPRIVILEGED
),
609 SD_BUS_METHOD("GetHomeByUID", "u", "ssussso", method_get_home_by_uid
, SD_BUS_VTABLE_UNPRIVILEGED
),
610 SD_BUS_METHOD("GetUserRecordByName", "s", "sbo", method_get_user_record_by_name
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
611 SD_BUS_METHOD("GetUserRecordByUID", "u", "sbo", method_get_user_record_by_uid
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
612 SD_BUS_METHOD("ListHomes", NULL
, "a(susussso)", method_list_homes
, SD_BUS_VTABLE_UNPRIVILEGED
),
614 /* The following methods directly execute an operation on a home area, without ref-counting, queing
615 * or anything, and are accessible through homectl. */
616 SD_BUS_METHOD("ActivateHome", "ss", NULL
, method_activate_home
, SD_BUS_VTABLE_SENSITIVE
),
617 SD_BUS_METHOD("DeactivateHome", "s", NULL
, method_deactivate_home
, 0),
618 SD_BUS_METHOD("RegisterHome", "s", NULL
, method_register_home
, SD_BUS_VTABLE_UNPRIVILEGED
), /* Add JSON record to homed, but don't create actual $HOME */
619 SD_BUS_METHOD("UnregisterHome", "s", NULL
, method_unregister_home
, SD_BUS_VTABLE_UNPRIVILEGED
), /* Remove JSON record from homed, but don't remove actual $HOME */
620 SD_BUS_METHOD("CreateHome", "s", NULL
, method_create_home
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
), /* Add JSON record, and create $HOME for it */
621 SD_BUS_METHOD("RealizeHome", "ss", NULL
, method_realize_home
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
), /* Create $HOME for already registered JSON entry */
622 SD_BUS_METHOD("RemoveHome", "s", NULL
, method_remove_home
, SD_BUS_VTABLE_UNPRIVILEGED
), /* Remove JSON record and remove $HOME */
623 SD_BUS_METHOD("FixateHome", "ss", NULL
, method_fixate_home
, SD_BUS_VTABLE_SENSITIVE
), /* Investigate $HOME and propagate contained JSON record into our database */
624 SD_BUS_METHOD("AuthenticateHome", "ss", NULL
, method_authenticate_home
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
), /* Just check credentials */
625 SD_BUS_METHOD("UpdateHome", "s", NULL
, method_update_home
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
), /* Update JSON record of existing user */
626 SD_BUS_METHOD("ResizeHome", "sts", NULL
, method_resize_home
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
627 SD_BUS_METHOD("ChangePasswordHome", "sss", NULL
, method_change_password_home
, SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
628 SD_BUS_METHOD("LockHome", "s", NULL
, method_lock_home
, 0), /* Prepare active home for system suspend: flush out passwords, suspend access */
629 SD_BUS_METHOD("UnlockHome", "ss", NULL
, method_unlock_home
, SD_BUS_VTABLE_SENSITIVE
), /* Make $HOME usable after system resume again */
631 /* The following methods implement ref-counted activation, and are what the PAM module calls (and
632 * what "homectl with" runs). In contrast to the methods above which fail if an operation is already
633 * being executed on a home directory, these ones will queue the request, and are thus more
634 * reliable. Moreover, they are a bit smarter: AcquireHome() will fixate, activate, unlock, or
635 * authenticate depending on the state of the home, so that the end result is always the same
636 * (i.e. the home directory is accessible), and we always validate the specified passwords. RefHome()
637 * will not authenticate, and thus only works if home is already active. */
638 SD_BUS_METHOD("AcquireHome", "ssb", "h", method_acquire_home
, SD_BUS_VTABLE_SENSITIVE
),
639 SD_BUS_METHOD("RefHome", "sb", "h", method_ref_home
, 0),
640 SD_BUS_METHOD("ReleaseHome", "s", NULL
, method_release_home
, 0),
642 /* An operation that acts on all homes that allow it */
643 SD_BUS_METHOD("LockAllHomes", NULL
, NULL
, method_lock_all_homes
, 0),
648 static int on_deferred_auto_login(sd_event_source
*s
, void *userdata
) {
649 Manager
*m
= userdata
;
654 m
->deferred_auto_login_event_source
= sd_event_source_unref(m
->deferred_auto_login_event_source
);
656 r
= sd_bus_emit_properties_changed(
658 "/org/freedesktop/home1",
659 "org.freedesktop.home1.Manager",
662 log_warning_errno(r
, "Failed to send AutoLogin property change event, ignoring: %m");
667 int bus_manager_emit_auto_login_changed(Manager
*m
) {
671 if (m
->deferred_auto_login_event_source
)
677 if (IN_SET(sd_event_get_state(m
->event
), SD_EVENT_FINISHED
, SD_EVENT_EXITING
))
680 r
= sd_event_add_defer(m
->event
, &m
->deferred_auto_login_event_source
, on_deferred_auto_login
, m
);
682 return log_error_errno(r
, "Failed to allocate auto login event source: %m");
684 r
= sd_event_source_set_priority(m
->deferred_auto_login_event_source
, SD_EVENT_PRIORITY_IDLE
+10);
686 log_warning_errno(r
, "Failed to tweak priority of event source, ignoring: %m");
688 (void) sd_event_source_set_description(m
->deferred_auto_login_event_source
, "deferred-auto-login");