]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/homed-manager-bus.c
user-util: add get{pw,gr}{uid,gid,name}_malloc() helpers
[thirdparty/systemd.git] / src / home / homed-manager-bus.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
70a5db58
LP
2
3#include <linux/capability.h>
4
5#include "alloc-util.h"
6#include "bus-common-errors.h"
7#include "bus-polkit.h"
8#include "format-util.h"
9#include "homed-bus.h"
10#include "homed-home-bus.h"
11#include "homed-manager-bus.h"
12#include "homed-manager.h"
13#include "strv.h"
14#include "user-record-sign.h"
15#include "user-record-util.h"
16#include "user-util.h"
17
18static int property_get_auto_login(
19 sd_bus *bus,
20 const char *path,
21 const char *interface,
22 const char *property,
23 sd_bus_message *reply,
24 void *userdata,
25 sd_bus_error *error) {
26
99534007 27 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
28 Home *h;
29 int r;
30
31 assert(bus);
32 assert(reply);
70a5db58
LP
33
34 r = sd_bus_message_open_container(reply, 'a', "(sso)");
35 if (r < 0)
36 return r;
37
90e74a66 38 HASHMAP_FOREACH(h, m->homes_by_name) {
5d2a48da 39 _cleanup_strv_free_ char **seats = NULL;
70a5db58 40 _cleanup_free_ char *home_path = NULL;
70a5db58
LP
41
42 r = home_auto_login(h, &seats);
43 if (r < 0) {
44 log_debug_errno(r, "Failed to determine whether home '%s' is candidate for auto-login, ignoring: %m", h->user_name);
45 continue;
46 }
47 if (!r)
48 continue;
49
50 r = bus_home_path(h, &home_path);
51 if (r < 0)
52 return log_error_errno(r, "Failed to generate home bus path: %m");
53
54 STRV_FOREACH(s, seats) {
55 r = sd_bus_message_append(reply, "(sso)", h->user_name, *s, home_path);
56 if (r < 0)
57 return r;
58 }
59 }
60
61 return sd_bus_message_close_container(reply);
62}
63
0e80e355
LP
64static int lookup_user_name(
65 Manager *m,
66 sd_bus_message *message,
67 const char *user_name,
68 sd_bus_error *error,
69 Home **ret) {
70
71 Home *h;
72 int r;
73
74 assert(m);
75 assert(message);
76 assert(user_name);
77 assert(ret);
78
79 if (isempty(user_name)) {
80 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
81 uid_t uid;
82
83 /* If an empty user name is specified, then identify caller's EUID and find home by that. */
84
85 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
86 if (r < 0)
87 return r;
88
89 r = sd_bus_creds_get_euid(creds, &uid);
90 if (r < 0)
91 return r;
92
93 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
94 if (!h)
95 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "Client's UID " UID_FMT " not managed.", uid);
96
97 } else {
98
99 if (!valid_user_group_name(user_name, 0))
100 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
101
102 h = hashmap_get(m->homes_by_name, user_name);
103 if (!h)
104 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
105 }
106
107 *ret = h;
108 return 0;
109}
110
70a5db58
LP
111static int method_get_home_by_name(
112 sd_bus_message *message,
113 void *userdata,
114 sd_bus_error *error) {
115
116 _cleanup_free_ char *path = NULL;
117 const char *user_name;
99534007 118 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
119 Home *h;
120 int r;
121
122 assert(message);
70a5db58
LP
123
124 r = sd_bus_message_read(message, "s", &user_name);
125 if (r < 0)
126 return r;
70a5db58 127
0e80e355
LP
128 r = lookup_user_name(m, message, user_name, error, &h);
129 if (r < 0)
130 return r;
70a5db58
LP
131
132 r = bus_home_path(h, &path);
133 if (r < 0)
134 return r;
135
136 return sd_bus_reply_method_return(
137 message, "usussso",
138 (uint32_t) h->uid,
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,
144 path);
145}
146
147static int method_get_home_by_uid(
148 sd_bus_message *message,
149 void *userdata,
150 sd_bus_error *error) {
151
152 _cleanup_free_ char *path = NULL;
99534007 153 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
154 uint32_t uid;
155 int r;
156 Home *h;
157
158 assert(message);
70a5db58
LP
159
160 r = sd_bus_message_read(message, "u", &uid);
161 if (r < 0)
162 return r;
163 if (!uid_is_valid(uid))
164 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
165
166 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
167 if (!h)
168 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
169
170 /* Note that we don't use bus_home_path() here, but build the path manually, since if we are queried
171 * for a UID we should also generate the bus path with a UID, and bus_home_path() uses our more
172 * typical bus path by name. */
173 if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
174 return -ENOMEM;
175
176 return sd_bus_reply_method_return(
177 message, "ssussso",
178 h->user_name,
179 home_state_to_string(home_get_state(h)),
180 h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
181 h->record ? user_record_real_name(h->record) : NULL,
182 h->record ? user_record_home_directory(h->record) : NULL,
183 h->record ? user_record_shell(h->record) : NULL,
184 path);
185}
186
187static int method_list_homes(
188 sd_bus_message *message,
189 void *userdata,
190 sd_bus_error *error) {
191
192 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
99534007 193 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
194 Home *h;
195 int r;
196
197 assert(message);
70a5db58
LP
198
199 r = sd_bus_message_new_method_return(message, &reply);
200 if (r < 0)
201 return r;
202
203 r = sd_bus_message_open_container(reply, 'a', "(susussso)");
204 if (r < 0)
205 return r;
206
90e74a66 207 HASHMAP_FOREACH(h, m->homes_by_uid) {
70a5db58
LP
208 _cleanup_free_ char *path = NULL;
209
210 r = bus_home_path(h, &path);
211 if (r < 0)
212 return r;
213
214 r = sd_bus_message_append(
215 reply, "(susussso)",
216 h->user_name,
217 (uint32_t) h->uid,
218 home_state_to_string(home_get_state(h)),
219 h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
220 h->record ? user_record_real_name(h->record) : NULL,
221 h->record ? user_record_home_directory(h->record) : NULL,
222 h->record ? user_record_shell(h->record) : NULL,
223 path);
224 if (r < 0)
225 return r;
226 }
227
228 r = sd_bus_message_close_container(reply);
229 if (r < 0)
230 return r;
231
232 return sd_bus_send(NULL, reply, NULL);
233}
234
235static int method_get_user_record_by_name(
236 sd_bus_message *message,
237 void *userdata,
238 sd_bus_error *error) {
239
240 _cleanup_free_ char *json = NULL, *path = NULL;
99534007 241 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
242 const char *user_name;
243 bool incomplete;
244 Home *h;
245 int r;
246
247 assert(message);
70a5db58
LP
248
249 r = sd_bus_message_read(message, "s", &user_name);
250 if (r < 0)
251 return r;
70a5db58 252
0e80e355
LP
253 r = lookup_user_name(m, message, user_name, error, &h);
254 if (r < 0)
255 return r;
70a5db58
LP
256
257 r = bus_home_get_record_json(h, message, &json, &incomplete);
258 if (r < 0)
259 return r;
260
261 r = bus_home_path(h, &path);
262 if (r < 0)
263 return r;
264
265 return sd_bus_reply_method_return(
266 message, "sbo",
267 json,
268 incomplete,
269 path);
270}
271
272static int method_get_user_record_by_uid(
273 sd_bus_message *message,
274 void *userdata,
275 sd_bus_error *error) {
276
277 _cleanup_free_ char *json = NULL, *path = NULL;
99534007 278 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
279 bool incomplete;
280 uint32_t uid;
281 Home *h;
282 int r;
283
284 assert(message);
70a5db58
LP
285
286 r = sd_bus_message_read(message, "u", &uid);
287 if (r < 0)
288 return r;
289 if (!uid_is_valid(uid))
290 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
291
292 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
293 if (!h)
294 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
295
296 r = bus_home_get_record_json(h, message, &json, &incomplete);
297 if (r < 0)
298 return r;
299
300 if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
301 return -ENOMEM;
302
303 return sd_bus_reply_method_return(
304 message, "sbo",
305 json,
306 incomplete,
307 path);
308}
309
310static int generic_home_method(
311 Manager *m,
312 sd_bus_message *message,
313 sd_bus_message_handler_t handler,
314 sd_bus_error *error) {
315
316 const char *user_name;
317 Home *h;
318 int r;
319
faef9ba2
LP
320 assert(m);
321 assert(message);
322 assert(handler);
323
70a5db58
LP
324 r = sd_bus_message_read(message, "s", &user_name);
325 if (r < 0)
326 return r;
327
0e80e355
LP
328 r = lookup_user_name(m, message, user_name, error, &h);
329 if (r < 0)
330 return r;
70a5db58
LP
331
332 return handler(message, h, error);
333}
334
335static int method_activate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
336 return generic_home_method(userdata, message, bus_home_method_activate, error);
337}
338
339static int method_deactivate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
340 return generic_home_method(userdata, message, bus_home_method_deactivate, error);
341}
342
343static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd_bus_error *error) {
344 _cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
70a5db58
LP
345 bool signed_locally;
346 Home *other;
347 int r;
348
349 assert(m);
350 assert(hr);
351 assert(ret);
352
353 r = user_record_is_supported(hr, error);
354 if (r < 0)
355 return r;
356
357 other = hashmap_get(m->homes_by_name, hr->user_name);
358 if (other)
359 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists already, refusing.", hr->user_name);
360
75673cd8
LP
361 r = getpwnam_malloc(hr->user_name, /* ret= */ NULL);
362 if (r >= 0)
70a5db58 363 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);
75673cd8
LP
364 if (r != -ESRCH)
365 return r;
70a5db58 366
75673cd8
LP
367 r = getgrnam_malloc(hr->user_name, /* ret= */ NULL);
368 if (r >= 0)
70a5db58 369 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);
75673cd8
LP
370 if (r != -ESRCH)
371 return r;
70a5db58
LP
372
373 r = manager_verify_user_record(m, hr);
374 switch (r) {
375
376 case USER_RECORD_UNSIGNED:
377 /* If the record is unsigned, then let's sign it with our own key */
378 r = manager_sign_user_record(m, hr, &signed_hr, error);
379 if (r < 0)
380 return r;
381
382 hr = signed_hr;
383 _fallthrough_;
384
385 case USER_RECORD_SIGNED_EXCLUSIVE:
386 signed_locally = true;
387 break;
388
389 case USER_RECORD_SIGNED:
390 case USER_RECORD_FOREIGN:
391 signed_locally = false;
392 break;
393
394 case -ENOKEY:
395 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);
396
397 default:
398 return sd_bus_error_set_errnof(error, r, "Failed to validate signature for '%s': %m", hr->user_name);
399 }
400
401 if (uid_is_valid(hr->uid)) {
75673cd8
LP
402 _cleanup_free_ struct passwd *pw = NULL;
403 _cleanup_free_ struct group *gr = NULL;
404
70a5db58
LP
405 other = hashmap_get(m->homes_by_uid, UID_TO_PTR(hr->uid));
406 if (other)
407 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);
408
75673cd8
LP
409 r = getpwuid_malloc(hr->uid, &pw);
410 if (r >= 0)
70a5db58 411 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);
75673cd8
LP
412 if (r != -ESRCH)
413 return r;
70a5db58 414
75673cd8
LP
415 r = getgrgid_malloc(hr->uid, &gr);
416 if (r >= 0)
70a5db58 417 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);
75673cd8
LP
418 if (r != -ESRCH)
419 return r;
70a5db58
LP
420 } else {
421 r = manager_augment_record_with_uid(m, hr);
422 if (r < 0)
423 return sd_bus_error_set_errnof(error, r, "Failed to acquire UID for '%s': %m", hr->user_name);
424 }
425
426 r = home_new(m, hr, NULL, ret);
427 if (r < 0)
428 return r;
429
430 (*ret)->signed_locally = signed_locally;
431 return r;
432}
433
434static int method_register_home(
435 sd_bus_message *message,
436 void *userdata,
437 sd_bus_error *error) {
438
439 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
99534007 440 Manager *m = ASSERT_PTR(userdata);
fc447921 441 _cleanup_(home_freep) Home *h = NULL;
70a5db58
LP
442 int r;
443
444 assert(message);
70a5db58 445
bfc0cc1a 446 r = bus_message_read_home_record(message, USER_RECORD_LOAD_EMBEDDED|USER_RECORD_PERMISSIVE, &hr, error);
70a5db58
LP
447 if (r < 0)
448 return r;
449
450 r = bus_verify_polkit_async(
451 message,
70a5db58 452 "org.freedesktop.home1.create-home",
7b36fb9f 453 /* details= */ NULL,
70a5db58
LP
454 &m->polkit_registry,
455 error);
456 if (r < 0)
457 return r;
458 if (r == 0)
459 return 1; /* Will call us back */
460
461 r = validate_and_allocate_home(m, hr, &h, error);
462 if (r < 0)
463 return r;
464
465 r = home_save_record(h);
fc447921 466 if (r < 0)
70a5db58 467 return r;
fc447921
DT
468
469 TAKE_PTR(h);
70a5db58
LP
470
471 return sd_bus_reply_method_return(message, NULL);
472}
473
474static int method_unregister_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
475 return generic_home_method(userdata, message, bus_home_method_unregister, error);
476}
477
478static int method_create_home(
479 sd_bus_message *message,
480 void *userdata,
481 sd_bus_error *error) {
482
483 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
99534007 484 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
485 Home *h;
486 int r;
487
488 assert(message);
70a5db58
LP
489
490 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);
491 if (r < 0)
492 return r;
493
494 r = bus_verify_polkit_async(
495 message,
70a5db58 496 "org.freedesktop.home1.create-home",
7b36fb9f 497 /* details= */ NULL,
70a5db58
LP
498 &m->polkit_registry,
499 error);
500 if (r < 0)
501 return r;
502 if (r == 0)
503 return 1; /* Will call us back */
504
505 r = validate_and_allocate_home(m, hr, &h, error);
506 if (r < 0)
507 return r;
508
509 r = home_create(h, hr, error);
510 if (r < 0)
511 goto fail;
512
513 assert(r == 0);
514 h->unregister_on_failure = true;
515 assert(!h->current_operation);
516
517 r = home_set_current_message(h, message);
518 if (r < 0)
519 return r;
520
521 return 1;
522
523fail:
524 (void) home_unlink_record(h);
525 h = home_free(h);
526 return r;
527}
528
529static int method_realize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
530 return generic_home_method(userdata, message, bus_home_method_realize, error);
531}
532
533static int method_remove_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
534 return generic_home_method(userdata, message, bus_home_method_remove, error);
535}
536
537static int method_fixate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
538 return generic_home_method(userdata, message, bus_home_method_fixate, error);
539}
540
541static int method_authenticate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
542 return generic_home_method(userdata, message, bus_home_method_authenticate, error);
543}
544
545static int method_update_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
546 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
99534007 547 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
548 Home *h;
549 int r;
550
551 assert(message);
70a5db58 552
bfc0cc1a 553 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);
70a5db58
LP
554 if (r < 0)
555 return r;
556
557 assert(hr->user_name);
558
559 h = hashmap_get(m->homes_by_name, hr->user_name);
560 if (!h)
561 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", hr->user_name);
562
563 return bus_home_method_update_record(h, message, hr, error);
564}
565
566static int method_resize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
567 return generic_home_method(userdata, message, bus_home_method_resize, error);
568}
569
570static int method_change_password_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
571 return generic_home_method(userdata, message, bus_home_method_change_password, error);
572}
573
574static int method_lock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
575 return generic_home_method(userdata, message, bus_home_method_lock, error);
576}
577
578static int method_unlock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
579 return generic_home_method(userdata, message, bus_home_method_unlock, error);
580}
581
582static int method_acquire_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
583 return generic_home_method(userdata, message, bus_home_method_acquire, error);
584}
585
586static int method_ref_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
587 return generic_home_method(userdata, message, bus_home_method_ref, error);
588}
589
590static int method_release_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
591 return generic_home_method(userdata, message, bus_home_method_release, error);
592}
593
594static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
595 _cleanup_(operation_unrefp) Operation *o = NULL;
596 bool waiting = false;
99534007 597 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
598 Home *h;
599 int r;
600
70a5db58
LP
601 /* This is called from logind when we are preparing for system suspend. We enqueue a lock operation
602 * for every suitable home we have and only when all of them completed we send a reply indicating
603 * completion. */
604
90e74a66 605 HASHMAP_FOREACH(h, m->homes_by_name) {
70a5db58 606
64b74c86 607 if (!home_shall_suspend(h))
70a5db58
LP
608 continue;
609
610 if (!o) {
611 o = operation_new(OPERATION_LOCK_ALL, message);
612 if (!o)
613 return -ENOMEM;
614 }
615
80ace4f2 616 log_info("Automatically locking home of user %s.", h->user_name);
70a5db58
LP
617
618 r = home_schedule_operation(h, o, error);
619 if (r < 0)
620 return r;
621
622 waiting = true;
623 }
624
625 if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will
d1f6e01e
LP
626 * be sent as soon as the last of the lock operations completed. */
627 return 1;
628
629 return sd_bus_reply_method_return(message, NULL);
630}
631
632static int method_deactivate_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
633 _cleanup_(operation_unrefp) Operation *o = NULL;
634 bool waiting = false;
99534007 635 Manager *m = ASSERT_PTR(userdata);
d1f6e01e
LP
636 Home *h;
637 int r;
638
d1f6e01e
LP
639 /* This is called from systemd-homed-activate.service's ExecStop= command to ensure that all home
640 * directories are shutdown before the system goes down. Note that we don't do this from
641 * systemd-homed.service itself since we want to allow restarting of it without tearing down all home
642 * directories. */
643
644 HASHMAP_FOREACH(h, m->homes_by_name) {
645
646 if (!o) {
647 o = operation_new(OPERATION_DEACTIVATE_ALL, message);
648 if (!o)
649 return -ENOMEM;
650 }
651
652 log_info("Automatically deactivating home of user %s.", h->user_name);
653
654 r = home_schedule_operation(h, o, error);
655 if (r < 0)
656 return r;
657
658 waiting = true;
659 }
660
661 if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will be
662 * sent as soon as the last of the deactivation operations completed. */
70a5db58
LP
663 return 1;
664
665 return sd_bus_reply_method_return(message, NULL);
666}
667
49505916 668static int method_rebalance(sd_bus_message *message, void *userdata, sd_bus_error *error) {
99534007 669 Manager *m = ASSERT_PTR(userdata);
49505916
LP
670 int r;
671
49505916
LP
672 r = manager_schedule_rebalance(m, /* immediately= */ true);
673 if (r == 0)
674 return sd_bus_reply_method_errorf(message, BUS_ERROR_REBALANCE_NOT_NEEDED, "No home directories need rebalancing.");
675 if (r < 0)
676 return r;
677
678 /* Keep a reference to this message, so that we can reply to it once we are done */
679 r = set_ensure_put(&m->rebalance_queued_method_calls, &bus_message_hash_ops, message);
680 if (r < 0)
681 return log_error_errno(r, "Failed to track rebalance bus message: %m");
682
683 sd_bus_message_ref(message);
684 return 1;
685}
686
cfd508a9 687static const sd_bus_vtable manager_vtable[] = {
70a5db58
LP
688 SD_BUS_VTABLE_START(0),
689
690 SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
691
9a814166
NK
692 SD_BUS_METHOD_WITH_ARGS("GetHomeByName",
693 SD_BUS_ARGS("s", user_name),
694 SD_BUS_RESULT("u", uid,
695 "s", home_state,
696 "u", gid,
697 "s", real_name,
698 "s", home_directory,
699 "s", shell,
700 "o", bus_path),
701 method_get_home_by_name,
702 SD_BUS_VTABLE_UNPRIVILEGED),
703 SD_BUS_METHOD_WITH_ARGS("GetHomeByUID",
704 SD_BUS_ARGS("u", uid),
705 SD_BUS_RESULT("s", user_name,
706 "s", home_state,
707 "u", gid,
708 "s", real_name,
709 "s", home_directory,
710 "s", shell,
711 "o", bus_path),
712 method_get_home_by_uid,
713 SD_BUS_VTABLE_UNPRIVILEGED),
714 SD_BUS_METHOD_WITH_ARGS("GetUserRecordByName",
715 SD_BUS_ARGS("s", user_name),
716 SD_BUS_RESULT("s", user_record, "b", incomplete, "o", bus_path),
717 method_get_user_record_by_name,
718 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
719 SD_BUS_METHOD_WITH_ARGS("GetUserRecordByUID",
720 SD_BUS_ARGS("u", uid),
721 SD_BUS_RESULT("s", user_record, "b", incomplete, "o", bus_path),
722 method_get_user_record_by_uid,
723 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
724 SD_BUS_METHOD_WITH_ARGS("ListHomes",
725 SD_BUS_NO_ARGS,
726 SD_BUS_RESULT("a(susussso)", home_areas),
727 method_list_homes,
728 SD_BUS_VTABLE_UNPRIVILEGED),
70a5db58 729
162392b7 730 /* The following methods directly execute an operation on a home area, without ref-counting, queueing
18143cd7 731 * or anything, and are accessible through homectl. */
9a814166
NK
732 SD_BUS_METHOD_WITH_ARGS("ActivateHome",
733 SD_BUS_ARGS("s", user_name, "s", secret),
734 SD_BUS_NO_RESULT,
735 method_activate_home,
736 SD_BUS_VTABLE_SENSITIVE),
737 SD_BUS_METHOD_WITH_ARGS("DeactivateHome",
738 SD_BUS_ARGS("s", user_name),
739 SD_BUS_NO_RESULT,
740 method_deactivate_home,
741 0),
cfd508a9
ZJS
742
743 /* Add the JSON record to homed, but don't create actual $HOME */
9a814166
NK
744 SD_BUS_METHOD_WITH_ARGS("RegisterHome",
745 SD_BUS_ARGS("s", user_record),
746 SD_BUS_NO_RESULT,
747 method_register_home,
748 SD_BUS_VTABLE_UNPRIVILEGED),
cfd508a9
ZJS
749
750 /* Remove the JSON record from homed, but don't remove actual $HOME */
9a814166
NK
751 SD_BUS_METHOD_WITH_ARGS("UnregisterHome",
752 SD_BUS_ARGS("s", user_name),
753 SD_BUS_NO_RESULT,
754 method_unregister_home,
755 SD_BUS_VTABLE_UNPRIVILEGED),
cfd508a9
ZJS
756
757 /* Add JSON record, and create $HOME for it */
9a814166
NK
758 SD_BUS_METHOD_WITH_ARGS("CreateHome",
759 SD_BUS_ARGS("s", user_record),
760 SD_BUS_NO_RESULT,
761 method_create_home,
762 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
763
764 /* Create $HOME for already registered JSON entry */
9a814166
NK
765 SD_BUS_METHOD_WITH_ARGS("RealizeHome",
766 SD_BUS_ARGS("s", user_name, "s", secret),
767 SD_BUS_NO_RESULT,
768 method_realize_home,
769 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
770
771 /* Remove the JSON record and remove $HOME */
9a814166
NK
772 SD_BUS_METHOD_WITH_ARGS("RemoveHome",
773 SD_BUS_ARGS("s", user_name),
774 SD_BUS_NO_RESULT,
775 method_remove_home,
776 SD_BUS_VTABLE_UNPRIVILEGED),
cfd508a9
ZJS
777
778 /* Investigate $HOME and propagate contained JSON record into our database */
9a814166
NK
779 SD_BUS_METHOD_WITH_ARGS("FixateHome",
780 SD_BUS_ARGS("s", user_name, "s", secret),
781 SD_BUS_NO_RESULT,
782 method_fixate_home,
783 SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
784
785 /* Just check credentials */
9a814166
NK
786 SD_BUS_METHOD_WITH_ARGS("AuthenticateHome",
787 SD_BUS_ARGS("s", user_name, "s", secret),
788 SD_BUS_NO_RESULT,
789 method_authenticate_home,
790 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
791
792 /* Update the JSON record of existing user */
9a814166
NK
793 SD_BUS_METHOD_WITH_ARGS("UpdateHome",
794 SD_BUS_ARGS("s", user_record),
795 SD_BUS_NO_RESULT,
796 method_update_home,
797 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
798
799 SD_BUS_METHOD_WITH_ARGS("ResizeHome",
800 SD_BUS_ARGS("s", user_name, "t", size, "s", secret),
801 SD_BUS_NO_RESULT,
802 method_resize_home,
803 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
804
805 SD_BUS_METHOD_WITH_ARGS("ChangePasswordHome",
806 SD_BUS_ARGS("s", user_name, "s", new_secret, "s", old_secret),
807 SD_BUS_NO_RESULT,
808 method_change_password_home,
809 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
810
811 /* Prepare active home for system suspend: flush out passwords, suspend access */
9a814166
NK
812 SD_BUS_METHOD_WITH_ARGS("LockHome",
813 SD_BUS_ARGS("s", user_name),
814 SD_BUS_NO_RESULT,
815 method_lock_home,
816 0),
cfd508a9
ZJS
817
818 /* Make $HOME usable after system resume again */
9a814166
NK
819 SD_BUS_METHOD_WITH_ARGS("UnlockHome",
820 SD_BUS_ARGS("s", user_name, "s", secret),
821 SD_BUS_NO_RESULT,
822 method_unlock_home,
823 SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
824
825 /* The following methods implement ref-counted activation, and are what the PAM module and "homectl
826 * with" use. In contrast to the methods above which fail if an operation is already being executed
827 * on a home directory, these ones will queue the request, and are thus more reliable. Moreover,
828 * they are a bit smarter: AcquireHome() will fixate, activate, unlock, or authenticate depending on
829 * the state of the home area, so that the end result is always the same (i.e. the home directory is
830 * accessible), and we always validate the specified passwords. RefHome() will not authenticate, and
831 * thus only works if the home area is already active. */
9a814166
NK
832 SD_BUS_METHOD_WITH_ARGS("AcquireHome",
833 SD_BUS_ARGS("s", user_name, "s", secret, "b", please_suspend),
834 SD_BUS_RESULT("h", send_fd),
835 method_acquire_home,
836 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
837 SD_BUS_METHOD_WITH_ARGS("RefHome",
838 SD_BUS_ARGS("s", user_name, "b", please_suspend),
839 SD_BUS_RESULT("h", send_fd),
840 method_ref_home,
841 0),
842 SD_BUS_METHOD_WITH_ARGS("ReleaseHome",
843 SD_BUS_ARGS("s", user_name),
844 SD_BUS_NO_RESULT,
845 method_release_home,
846 0),
70a5db58
LP
847
848 /* An operation that acts on all homes that allow it */
849 SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
d1f6e01e 850 SD_BUS_METHOD("DeactivateAllHomes", NULL, NULL, method_deactivate_all_homes, 0),
49505916 851 SD_BUS_METHOD("Rebalance", NULL, NULL, method_rebalance, 0),
70a5db58
LP
852
853 SD_BUS_VTABLE_END
854};
855
cfd508a9
ZJS
856const BusObjectImplementation manager_object = {
857 "/org/freedesktop/home1",
858 "org.freedesktop.home1.Manager",
859 .vtables = BUS_VTABLES(manager_vtable),
860 .children = BUS_IMPLEMENTATIONS(&home_object),
861};
862
70a5db58 863static int on_deferred_auto_login(sd_event_source *s, void *userdata) {
99534007 864 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
865 int r;
866
cf536638 867 m->deferred_auto_login_event_source = sd_event_source_disable_unref(m->deferred_auto_login_event_source);
70a5db58
LP
868
869 r = sd_bus_emit_properties_changed(
870 m->bus,
871 "/org/freedesktop/home1",
872 "org.freedesktop.home1.Manager",
873 "AutoLogin", NULL);
874 if (r < 0)
875 log_warning_errno(r, "Failed to send AutoLogin property change event, ignoring: %m");
876
877 return 0;
878}
879
880int bus_manager_emit_auto_login_changed(Manager *m) {
881 int r;
882 assert(m);
883
884 if (m->deferred_auto_login_event_source)
885 return 0;
886
887 if (!m->event)
888 return 0;
889
890 if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
891 return 0;
892
893 r = sd_event_add_defer(m->event, &m->deferred_auto_login_event_source, on_deferred_auto_login, m);
894 if (r < 0)
895 return log_error_errno(r, "Failed to allocate auto login event source: %m");
896
897 r = sd_event_source_set_priority(m->deferred_auto_login_event_source, SD_EVENT_PRIORITY_IDLE+10);
898 if (r < 0)
899 log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
900
901 (void) sd_event_source_set_description(m->deferred_auto_login_event_source, "deferred-auto-login");
902 return 1;
903}