1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "bus-common-errors.h"
8 #include "bus-object.h"
9 #include "bus-polkit.h"
11 #include "format-util.h"
13 #include "home-util.h"
14 #include "homed-bus.h"
15 #include "homed-home.h"
16 #include "homed-home-bus.h"
17 #include "homed-manager.h"
18 #include "homed-operation.h"
20 #include "string-util.h"
22 #include "user-record-util.h"
23 #include "user-util.h"
25 static int property_get_unix_record(
28 const char *interface
,
30 sd_bus_message
*reply
,
32 sd_bus_error
*error
) {
34 Home
*h
= ASSERT_PTR(userdata
);
39 return sd_bus_message_append(
43 h
->record
? (uint32_t) user_record_gid(h
->record
) : GID_INVALID
,
44 h
->record
? user_record_real_name(h
->record
) : NULL
,
45 h
->record
? user_record_home_directory(h
->record
) : NULL
,
46 h
->record
? user_record_shell(h
->record
) : NULL
);
49 static int property_get_state(
52 const char *interface
,
54 sd_bus_message
*reply
,
56 sd_bus_error
*error
) {
58 Home
*h
= ASSERT_PTR(userdata
);
63 return sd_bus_message_append(reply
, "s", home_state_to_string(home_get_state(h
)));
66 static int bus_home_client_is_trusted(Home
*h
, sd_bus_message
*message
, bool strict
) {
67 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
76 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_EUID
, &creds
);
80 r
= sd_bus_creds_get_euid(creds
, &euid
);
84 return (!strict
&& euid
== 0) || h
->uid
== euid
;
87 static int home_verify_polkit_async(
89 sd_bus_message
*message
,
92 sd_bus_error
*error
) {
99 const char *details
[] = {
100 "uid", FORMAT_UID(h
->uid
),
101 "username", h
->user_name
,
105 return bus_verify_polkit_async_full(
111 &h
->manager
->polkit_registry
,
115 int bus_home_get_record_json(
117 sd_bus_message
*message
,
119 bool *ret_incomplete
) {
121 _cleanup_(user_record_unrefp
) UserRecord
*augmented
= NULL
;
122 UserRecordLoadFlags flags
;
128 trusted
= bus_home_client_is_trusted(h
, message
, /* strict= */ false);
130 log_warning_errno(trusted
, "Failed to determine whether client is trusted, assuming untrusted.");
134 flags
= USER_RECORD_REQUIRE_REGULAR
|USER_RECORD_ALLOW_PER_MACHINE
|USER_RECORD_ALLOW_BINDING
|USER_RECORD_STRIP_SECRET
|USER_RECORD_ALLOW_STATUS
|USER_RECORD_ALLOW_SIGNATURE
|USER_RECORD_PERMISSIVE
;
136 flags
|= USER_RECORD_ALLOW_PRIVILEGED
;
138 flags
|= USER_RECORD_STRIP_PRIVILEGED
;
140 r
= home_augment_status(h
, flags
, &augmented
);
144 r
= sd_json_variant_format(augmented
->json
, 0, ret
);
149 *ret_incomplete
= augmented
->incomplete
;
154 static int property_get_user_record(
157 const char *interface
,
158 const char *property
,
159 sd_bus_message
*reply
,
161 sd_bus_error
*error
) {
163 _cleanup_free_
char *json
= NULL
;
164 Home
*h
= ASSERT_PTR(userdata
);
171 r
= bus_home_get_record_json(h
, sd_bus_get_current_message(bus
), &json
, &incomplete
);
175 return sd_bus_message_append(reply
, "(sb)", json
, incomplete
);
178 int bus_home_method_activate(
179 sd_bus_message
*message
,
181 sd_bus_error
*error
) {
183 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
184 Home
*h
= ASSERT_PTR(userdata
);
190 if_referenced
= endswith(sd_bus_message_get_member(message
), "IfReferenced");
192 r
= bus_verify_polkit_async_full(
194 "org.freedesktop.home1.activate-home",
198 &h
->manager
->polkit_registry
,
203 return 1; /* Will call us back */
205 r
= bus_message_read_secret(message
, &secret
, error
);
209 r
= home_activate(h
, if_referenced
, secret
, error
);
214 assert(!h
->current_operation
);
216 /* The operation is now in process, keep track of this message so that we can later reply to it. */
217 r
= home_set_current_message(h
, message
);
224 int bus_home_method_deactivate(
225 sd_bus_message
*message
,
227 sd_bus_error
*error
) {
229 Home
*h
= ASSERT_PTR(userdata
);
234 r
= home_deactivate(h
, false, error
);
239 assert(!h
->current_operation
);
241 r
= home_set_current_message(h
, message
);
248 int bus_home_method_unregister(
249 sd_bus_message
*message
,
251 sd_bus_error
*error
) {
253 Home
*h
= ASSERT_PTR(userdata
);
258 r
= home_verify_polkit_async(
261 "org.freedesktop.home1.remove-home",
267 return 1; /* Will call us back */
269 r
= home_unregister(h
, error
);
275 /* Note that home_unregister() destroyed 'h' here, so no more accesses */
277 return sd_bus_reply_method_return(message
, NULL
);
280 int bus_home_method_realize(
281 sd_bus_message
*message
,
283 sd_bus_error
*error
) {
285 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
286 Home
*h
= ASSERT_PTR(userdata
);
291 r
= bus_message_read_secret(message
, &secret
, error
);
295 r
= home_verify_polkit_async(
298 "org.freedesktop.home1.create-home",
304 return 1; /* Will call us back */
306 r
= home_create(h
, secret
, NULL
, 0, error
);
311 assert(!h
->current_operation
);
313 h
->unregister_on_failure
= false;
315 r
= home_set_current_message(h
, message
);
322 int bus_home_method_remove(
323 sd_bus_message
*message
,
325 sd_bus_error
*error
) {
327 Home
*h
= ASSERT_PTR(userdata
);
332 r
= home_verify_polkit_async(
335 "org.freedesktop.home1.remove-home",
341 return 1; /* Will call us back */
343 r
= home_remove(h
, error
);
346 if (r
> 0) /* Done already. Note that home_remove() destroyed 'h' here, so no more accesses */
347 return sd_bus_reply_method_return(message
, NULL
);
349 assert(!h
->current_operation
);
351 r
= home_set_current_message(h
, message
);
358 int bus_home_method_fixate(
359 sd_bus_message
*message
,
361 sd_bus_error
*error
) {
363 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
364 Home
*h
= ASSERT_PTR(userdata
);
369 r
= bus_message_read_secret(message
, &secret
, error
);
373 r
= home_fixate(h
, secret
, error
);
378 assert(!h
->current_operation
);
380 r
= home_set_current_message(h
, message
);
387 int bus_home_method_authenticate(
388 sd_bus_message
*message
,
390 sd_bus_error
*error
) {
392 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
393 Home
*h
= ASSERT_PTR(userdata
);
398 r
= bus_message_read_secret(message
, &secret
, error
);
402 r
= home_verify_polkit_async(
405 "org.freedesktop.home1.authenticate-home",
411 return 1; /* Will call us back */
413 r
= home_authenticate(h
, secret
, error
);
418 assert(!h
->current_operation
);
420 r
= home_set_current_message(h
, message
);
427 int bus_home_update_record(
429 sd_bus_message
*message
,
433 sd_bus_error
*error
) {
440 r
= user_record_is_supported(hr
, error
);
444 if ((flags
& ~SD_HOMED_UPDATE_FLAGS_ALL
) != 0)
445 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid flags provided.");
448 const char *failed
= NULL
;
449 r
= user_record_ensure_blob_manifest(hr
, blobs
, &failed
);
451 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Provided blob files do not correspond to blob manifest.");
453 return sd_bus_error_set_errnof(error
, r
, "Failed to generate hash for blob %s: %m", strnull(failed
));
456 relax_access
= user_record_self_changes_allowed(h
->record
, hr
);
457 if (relax_access
< 0) {
458 log_warning_errno(relax_access
, "Failed to determine if changes to user record are permitted, assuming not: %m");
459 relax_access
= false;
460 } else if (relax_access
) {
461 relax_access
= bus_home_client_is_trusted(h
, message
, /* strict= */ true);
462 if (relax_access
< 0) {
463 log_warning_errno(relax_access
, "Failed to determine whether client is trusted, assuming not: %m");
464 relax_access
= false;
468 r
= home_verify_polkit_async(
471 relax_access
? "org.freedesktop.home1.update-home-by-owner"
472 : "org.freedesktop.home1.update-home",
478 return 1; /* Will call us back */
480 r
= home_update(h
, hr
, blobs
, flags
, error
);
485 assert(!h
->current_operation
);
487 r
= home_set_current_message(h
, message
);
491 h
->current_operation
->call_flags
= flags
;
496 int bus_home_method_update(
497 sd_bus_message
*message
,
499 sd_bus_error
*error
) {
501 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
502 _cleanup_hashmap_free_ Hashmap
*blobs
= NULL
;
504 Home
*h
= ASSERT_PTR(userdata
);
509 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
|USER_RECORD_PERMISSIVE
, &hr
, error
);
513 if (endswith(sd_bus_message_get_member(message
), "Ex")) {
514 r
= bus_message_read_blobs(message
, &blobs
, error
);
518 r
= sd_bus_message_read(message
, "t", &flags
);
523 return bus_home_update_record(h
, message
, hr
, blobs
, flags
, error
);
526 int bus_home_method_resize(
527 sd_bus_message
*message
,
529 sd_bus_error
*error
) {
531 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
532 Home
*h
= ASSERT_PTR(userdata
);
538 r
= sd_bus_message_read(message
, "t", &sz
);
542 r
= bus_message_read_secret(message
, &secret
, error
);
546 r
= home_verify_polkit_async(
549 "org.freedesktop.home1.resize-home",
555 return 1; /* Will call us back */
557 r
= home_resize(h
, sz
, secret
, error
);
562 assert(!h
->current_operation
);
564 r
= home_set_current_message(h
, message
);
571 int bus_home_method_change_password(
572 sd_bus_message
*message
,
574 sd_bus_error
*error
) {
576 _cleanup_(user_record_unrefp
) UserRecord
*new_secret
= NULL
, *old_secret
= NULL
;
577 Home
*h
= ASSERT_PTR(userdata
);
582 r
= bus_message_read_secret(message
, &new_secret
, error
);
586 r
= bus_message_read_secret(message
, &old_secret
, error
);
590 r
= home_verify_polkit_async(
593 "org.freedesktop.home1.passwd-home",
594 h
->uid
, /* Always let a user change their own password. Safe b/c homework will always re-check password */
599 return 1; /* Will call us back */
601 r
= home_passwd(h
, new_secret
, old_secret
, error
);
606 assert(!h
->current_operation
);
608 r
= home_set_current_message(h
, message
);
615 int bus_home_method_lock(
616 sd_bus_message
*message
,
618 sd_bus_error
*error
) {
620 Home
*h
= ASSERT_PTR(userdata
);
625 r
= home_lock(h
, error
);
628 if (r
> 0) /* Done */
629 return sd_bus_reply_method_return(message
, NULL
);
631 /* The operation is now in process, keep track of this message so that we can later reply to it. */
632 assert(!h
->current_operation
);
634 r
= home_set_current_message(h
, message
);
641 int bus_home_method_unlock(
642 sd_bus_message
*message
,
644 sd_bus_error
*error
) {
646 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
647 Home
*h
= ASSERT_PTR(userdata
);
652 r
= bus_message_read_secret(message
, &secret
, error
);
656 r
= home_unlock(h
, secret
, error
);
661 assert(!h
->current_operation
);
663 /* The operation is now in process, keep track of this message so that we can later reply to it. */
664 r
= home_set_current_message(h
, message
);
671 int bus_home_method_acquire(
672 sd_bus_message
*message
,
674 sd_bus_error
*error
) {
676 _cleanup_(user_record_unrefp
) UserRecord
*secret
= NULL
;
677 _cleanup_(operation_unrefp
) Operation
*o
= NULL
;
678 _cleanup_close_
int fd
= -EBADF
;
679 int r
, please_suspend
;
680 Home
*h
= ASSERT_PTR(userdata
);
684 r
= bus_message_read_secret(message
, &secret
, error
);
688 r
= sd_bus_message_read(message
, "b", &please_suspend
);
692 /* This operation might not be something we can executed immediately, hence queue it */
693 fd
= home_create_fifo(h
, please_suspend
);
695 return sd_bus_reply_method_errnof(message
, fd
, "Failed to allocate FIFO for %s: %m", h
->user_name
);
697 o
= operation_new(OPERATION_ACQUIRE
, message
);
701 o
->secret
= TAKE_PTR(secret
);
702 o
->send_fd
= TAKE_FD(fd
);
704 r
= home_schedule_operation(h
, o
, error
);
711 int bus_home_method_ref(
712 sd_bus_message
*message
,
714 sd_bus_error
*error
) {
716 _cleanup_close_
int fd
= -EBADF
;
717 Home
*h
= ASSERT_PTR(userdata
);
718 int please_suspend
, r
;
723 /* In unrestricted mode we'll add a reference to the home even if it's not active */
724 unrestricted
= strstr(sd_bus_message_get_member(message
), "Unrestricted");
726 r
= sd_bus_message_read(message
, "b", &please_suspend
);
733 state
= home_get_state(h
);
737 return sd_bus_error_setf(error
, BUS_ERROR_HOME_ABSENT
, "Home %s is currently missing or not plugged in.", h
->user_name
);
741 return sd_bus_error_setf(error
, BUS_ERROR_HOME_NOT_ACTIVE
, "Home %s not active.", h
->user_name
);
743 return sd_bus_error_setf(error
, BUS_ERROR_HOME_LOCKED
, "Home %s is currently locked.", h
->user_name
);
745 if (HOME_STATE_IS_ACTIVE(state
))
748 return sd_bus_error_setf(error
, BUS_ERROR_HOME_BUSY
, "An operation on home %s is currently being executed.", h
->user_name
);
752 fd
= home_create_fifo(h
, please_suspend
);
754 return sd_bus_reply_method_errnof(message
, fd
, "Failed to allocate FIFO for %s: %m", h
->user_name
);
756 return sd_bus_reply_method_return(message
, "h", fd
);
759 int bus_home_method_release(
760 sd_bus_message
*message
,
762 sd_bus_error
*error
) {
764 _cleanup_(operation_unrefp
) Operation
*o
= NULL
;
765 Home
*h
= ASSERT_PTR(userdata
);
770 o
= operation_new(OPERATION_RELEASE
, message
);
774 r
= home_schedule_operation(h
, o
, error
);
781 /* We map a uid_t as uint32_t bus property, let's ensure this is safe. */
782 assert_cc(sizeof(uid_t
) == sizeof(uint32_t));
784 int bus_home_path(Home
*h
, char **ret
) {
787 return sd_bus_path_encode("/org/freedesktop/home1/home", h
->user_name
, ret
);
790 static int bus_home_object_find(
793 const char *interface
,
796 sd_bus_error
*error
) {
798 _cleanup_free_
char *e
= NULL
;
799 Manager
*m
= userdata
;
804 r
= sd_bus_path_decode(path
, "/org/freedesktop/home1/home", &e
);
808 if (parse_uid(e
, &uid
) >= 0)
809 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR(uid
));
811 r
= manager_get_home_by_name(m
, e
, &h
);
822 static int bus_home_node_enumerator(
827 sd_bus_error
*error
) {
829 _cleanup_strv_free_
char **l
= NULL
;
830 Manager
*m
= userdata
;
837 l
= new0(char*, hashmap_size(m
->homes_by_uid
) + 1);
841 HASHMAP_FOREACH(h
, m
->homes_by_uid
) {
842 r
= bus_home_path(h
, l
+ k
);
849 *nodes
= TAKE_PTR(l
);
853 const sd_bus_vtable home_vtable
[] = {
854 SD_BUS_VTABLE_START(0),
856 SD_BUS_PROPERTY("UserName", "s",
857 NULL
, offsetof(Home
, user_name
),
858 SD_BUS_VTABLE_PROPERTY_CONST
),
859 SD_BUS_PROPERTY("UID", "u",
860 NULL
, offsetof(Home
, uid
),
861 SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
862 SD_BUS_PROPERTY("UnixRecord", "(suusss)",
863 property_get_unix_record
, 0,
864 SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
865 SD_BUS_PROPERTY("State", "s",
866 property_get_state
, 0,
868 SD_BUS_PROPERTY("UserRecord", "(sb)",
869 property_get_user_record
, 0,
870 SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
|SD_BUS_VTABLE_SENSITIVE
),
872 SD_BUS_METHOD_WITH_ARGS("Activate",
873 SD_BUS_ARGS("s", secret
),
875 bus_home_method_activate
,
876 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
877 SD_BUS_METHOD_WITH_ARGS("ActivateIfReferenced",
878 SD_BUS_ARGS("s", secret
),
880 bus_home_method_activate
,
881 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
882 SD_BUS_METHOD("Deactivate", NULL
, NULL
, bus_home_method_deactivate
, 0),
883 SD_BUS_METHOD("Unregister", NULL
, NULL
, bus_home_method_unregister
, SD_BUS_VTABLE_UNPRIVILEGED
),
884 SD_BUS_METHOD_WITH_ARGS("Realize",
885 SD_BUS_ARGS("s", secret
),
887 bus_home_method_realize
,
888 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
890 SD_BUS_METHOD("Remove", NULL
, NULL
, bus_home_method_remove
, SD_BUS_VTABLE_UNPRIVILEGED
),
891 SD_BUS_METHOD_WITH_ARGS("Fixate",
892 SD_BUS_ARGS("s", secret
),
894 bus_home_method_fixate
,
895 SD_BUS_VTABLE_SENSITIVE
),
896 SD_BUS_METHOD_WITH_ARGS("Authenticate",
897 SD_BUS_ARGS("s", secret
),
899 bus_home_method_authenticate
,
900 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
901 SD_BUS_METHOD_WITH_ARGS("Update",
902 SD_BUS_ARGS("s", user_record
),
904 bus_home_method_update
,
905 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
906 SD_BUS_METHOD_WITH_ARGS("UpdateEx",
907 SD_BUS_ARGS("s", user_record
, "a{sh}", blobs
, "t", flags
),
909 bus_home_method_update
,
910 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
911 SD_BUS_METHOD_WITH_ARGS("Resize",
912 SD_BUS_ARGS("t", size
, "s", secret
),
914 bus_home_method_resize
,
915 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
916 SD_BUS_METHOD_WITH_ARGS("ChangePassword",
917 SD_BUS_ARGS("s", new_secret
, "s", old_secret
),
919 bus_home_method_change_password
,
920 SD_BUS_VTABLE_UNPRIVILEGED
|SD_BUS_VTABLE_SENSITIVE
),
921 SD_BUS_METHOD("Lock", NULL
, NULL
, bus_home_method_lock
, 0),
922 SD_BUS_METHOD_WITH_ARGS("Unlock",
923 SD_BUS_ARGS("s", secret
),
925 bus_home_method_unlock
,
926 SD_BUS_VTABLE_SENSITIVE
),
927 SD_BUS_METHOD_WITH_ARGS("Acquire",
928 SD_BUS_ARGS("s", secret
, "b", please_suspend
),
929 SD_BUS_RESULT("h", send_fd
),
930 bus_home_method_acquire
,
931 SD_BUS_VTABLE_SENSITIVE
),
932 SD_BUS_METHOD_WITH_ARGS("Ref",
933 SD_BUS_ARGS("b", please_suspend
),
934 SD_BUS_RESULT("h", send_fd
),
937 SD_BUS_METHOD_WITH_ARGS("RefUnrestricted",
938 SD_BUS_ARGS("b", please_suspend
),
939 SD_BUS_RESULT("h", send_fd
),
942 SD_BUS_METHOD("Release", NULL
, NULL
, bus_home_method_release
, 0),
946 const BusObjectImplementation home_object
= {
947 "/org/freedesktop/home1/home",
948 "org.freedesktop.home1.Home",
949 .fallback_vtables
= BUS_FALLBACK_VTABLES({home_vtable
, bus_home_object_find
}),
950 .node_enumerator
= bus_home_node_enumerator
,
954 static int on_deferred_change(sd_event_source
*s
, void *userdata
) {
955 _cleanup_free_
char *path
= NULL
;
956 Home
*h
= ASSERT_PTR(userdata
);
959 h
->deferred_change_event_source
= sd_event_source_disable_unref(h
->deferred_change_event_source
);
961 r
= bus_home_path(h
, &path
);
963 log_warning_errno(r
, "Failed to generate home bus path, ignoring: %m");
968 r
= sd_bus_emit_properties_changed_strv(h
->manager
->bus
, path
, "org.freedesktop.home1.Home", NULL
);
970 r
= sd_bus_emit_object_added(h
->manager
->bus
, path
);
972 log_warning_errno(r
, "Failed to send home change event, ignoring: %m");
979 int bus_home_emit_change(Home
*h
) {
984 if (h
->deferred_change_event_source
)
987 if (!h
->manager
->event
)
990 if (IN_SET(sd_event_get_state(h
->manager
->event
), SD_EVENT_FINISHED
, SD_EVENT_EXITING
))
993 r
= sd_event_add_defer(h
->manager
->event
, &h
->deferred_change_event_source
, on_deferred_change
, h
);
995 return log_error_errno(r
, "Failed to allocate deferred change event source: %m");
997 r
= sd_event_source_set_priority(h
->deferred_change_event_source
, SD_EVENT_PRIORITY_IDLE
+5);
999 log_warning_errno(r
, "Failed to tweak priority of event source, ignoring: %m");
1001 (void) sd_event_source_set_description(h
->deferred_change_event_source
, "deferred-change-event");
1005 int bus_home_emit_remove(Home
*h
) {
1006 _cleanup_free_
char *path
= NULL
;
1017 if (!h
->manager
->bus
)
1020 r
= bus_home_path(h
, &path
);
1024 r
= sd_bus_emit_object_removed(h
->manager
->bus
, path
);
1028 h
->announced
= false;